/**
   * Record time-stamped information about the activity of the connection. This information can
   * originate from either the connector or from the framework. The reason it is here is that it is
   * viewed as 'belonging' to an individual connection, and is segregated accordingly.
   *
   * @param connectionName is the connection to which the record belongs. If the connection is
   *     deleted, the corresponding records will also be deleted. Cannot be null.
   * @param startTime is either null or the time since the start of epoch in milliseconds (Jan 1,
   *     1970). Every activity has an associated time; the startTime field records when the activity
   *     began. A null value indicates that the start time and the finishing time are the same.
   * @param activityType is a string which is fully interpretable only in the context of the
   *     connector involved, which is used to categorize what kind of activity is being recorded.
   *     For example, a web connector might record a "fetch document" activity, while the framework
   *     might record "ingest document", "job start", "job finish", "job abort", etc. Cannot be
   *     null.
   * @param dataSize is the number of bytes of data involved in the activity, or null if not
   *     applicable.
   * @param entityIdentifier is a (possibly long) string which identifies the object involved in the
   *     history record. The interpretation of this field will differ from connector to connector.
   *     May be null.
   * @param resultCode contains a terse description of the result of the activity. The description
   *     is limited in size to 255 characters, and can be interpreted only in the context of the
   *     current connector. May be null.
   * @param resultDescription is a (possibly long) human-readable string which adds detail, if
   *     required, to the result described in the resultCode field. This field is not meant to be
   *     queried on. May be null.
   * @param childIdentifiers is a set of child entity identifiers associated with this activity. May
   *     be null.
   */
  public void recordHistory(
      String connectionName,
      Long startTime,
      String activityType,
      Long dataSize,
      String entityIdentifier,
      String resultCode,
      String resultDescription,
      String[] childIdentifiers)
      throws ManifoldCFException {
    long endTimeValue = System.currentTimeMillis();
    long startTimeValue;
    if (startTime == null) startTimeValue = endTimeValue - 1L;
    else {
      startTimeValue = startTime.longValue();
      if (startTimeValue == endTimeValue) startTimeValue -= 1L; // Zero-time events are not allowed.
    }

    long dataSizeValue;
    if (dataSize == null) dataSizeValue = 0L;
    else dataSizeValue = dataSize.longValue();

    Long rowID =
        historyManager.addRow(
            connectionName,
            startTimeValue,
            endTimeValue,
            dataSizeValue,
            activityType,
            entityIdentifier,
            resultCode,
            resultDescription);
    // child identifiers are not stored, for now.
    // MHL
  }
 /**
  * Generate a report, listing the result bucket and identifier bucket. The records selected for
  * this report are based on the filtering criteria object passed into this method. The record
  * order is based on the sorting criteria object passed into this method. The result code bucket
  * description is specified by a bucket description object. The identifier bucket description is
  * specified by a bucket description object. The resultset returned should have the following
  * columns: "resultcodebucket","idbucket".
  *
  * @param connectionName is the name of the connection.
  * @param criteria is the filtering criteria, which selects the records of interest.
  * @param sort is the sorting order, which can specify sort based on the result columns.
  * @param resultCodeBucket is the description of the bucket based on processed result codes.
  * @param idBucket is the description of the bucket based on processed entity identifiers.
  * @param startRow is the first row to include (beginning with 0)
  * @param maxRowCount is the maximum number of rows to include.
  */
 public IResultSet genHistoryResultCodes(
     String connectionName,
     FilterCriteria criteria,
     SortOrder sort,
     BucketDescription resultCodeBucket,
     BucketDescription idBucket,
     int startRow,
     int maxRowCount)
     throws ManifoldCFException {
   return historyManager.resultCodesReport(
       connectionName, criteria, sort, resultCodeBucket, idBucket, startRow, maxRowCount);
 }
 /**
  * Generate a report, listing the start time, bytes processed, and identifier bucket, given a time
  * slice (interval) size. The records selected for this report are based on the filtering criteria
  * object passed into this method. The record order is based on the sorting criteria object passed
  * into this method. The identifier bucket description is specified by the bucket description
  * object. The resultset returned should have the following columns:
  * "starttime","endtime","bytecount","idbucket".
  *
  * @param connectionName is the name of the connection.
  * @param criteria is the filtering criteria, which selects the records of interest.
  * @param sort is the sorting order, which can specify sort based on the result columns.
  * @param idBucket is the description of the bucket based on processed entity identifiers.
  * @param interval is the time interval, in milliseconds, to locate. There will be one row in the
  *     resultset for each distinct idBucket value, and the returned activity count will the
  *     maximum found over the specified interval size.
  * @param startRow is the first row to include (beginning with 0)
  * @param maxRowCount is the maximum number of rows to include.
  */
 public IResultSet genHistoryByteCount(
     String connectionName,
     FilterCriteria criteria,
     SortOrder sort,
     BucketDescription idBucket,
     long interval,
     int startRow,
     int maxRowCount)
     throws ManifoldCFException {
   return historyManager.maxByteCountReport(
       connectionName, criteria, sort, idBucket, interval, startRow, maxRowCount);
 }
 /** Uninstall the manager. */
 public void deinstall() throws ManifoldCFException {
   beginTransaction();
   try {
     throttleSpecManager.deinstall();
     historyManager.deinstall();
     performDrop(null);
   } catch (ManifoldCFException e) {
     signalRollback();
     throw e;
   } catch (Error e) {
     signalRollback();
     throw e;
   } finally {
     endTransaction();
   }
 }
  /**
   * Delete a repository connection.
   *
   * @param name is the name of the connection to delete. If the name does not exist, no error is
   *     returned.
   */
  public void delete(String name) throws ManifoldCFException {
    // Grab a job manager handle.  We will need to check if any jobs refer to this connection.
    IJobManager jobManager = JobManagerFactory.make(threadContext);

    StringSetBuffer ssb = new StringSetBuffer();
    ssb.add(getRepositoryConnectionsKey());
    ssb.add(getRepositoryConnectionKey(name));
    StringSet cacheKeys = new StringSet(ssb);
    ICacheHandle ch = cacheManager.enterCache(null, cacheKeys, getTransactionID());
    try {
      beginTransaction();
      try {
        // Check if any jobs refer to this connection name
        if (jobManager.checkIfReference(name))
          throw new ManifoldCFException(
              "Can't delete repository connection '" + name + "': existing jobs refer to it");
        ManifoldCF.noteConfigurationChange();
        throttleSpecManager.deleteRows(name);
        historyManager.deleteOwner(name);
        ArrayList params = new ArrayList();
        String query =
            buildConjunctionClause(
                params, new ClauseDescription[] {new UnitaryClause(nameField, name)});
        performDelete("WHERE " + query, params, null);
        cacheManager.invalidateKeys(ch);
      } catch (ManifoldCFException e) {
        signalRollback();
        throw e;
      } catch (Error e) {
        signalRollback();
        throw e;
      } finally {
        endTransaction();
      }
    } finally {
      cacheManager.leaveCache(ch);
    }
  }
  /** Install the manager. */
  public void install() throws ManifoldCFException {
    // First, get the authority manager table name and name column
    IAuthorityGroupManager authMgr = AuthorityGroupManagerFactory.make(threadContext);

    // Always use a loop, and no transaction, as we may need to retry due to upgrade
    while (true) {
      Map existing = getTableSchema(null, null);
      if (existing == null) {
        // Install the "objects" table.
        HashMap map = new HashMap();
        map.put(nameField, new ColumnDescription("VARCHAR(32)", true, false, null, null, false));
        map.put(
            descriptionField,
            new ColumnDescription("VARCHAR(255)", false, true, null, null, false));
        map.put(
            classNameField, new ColumnDescription("VARCHAR(255)", false, false, null, null, false));
        map.put(
            groupNameField,
            new ColumnDescription(
                "VARCHAR(32)",
                false,
                true,
                authMgr.getTableName(),
                authMgr.getGroupNameColumn(),
                false));
        map.put(maxCountField, new ColumnDescription("BIGINT", false, false, null, null, false));
        map.put(configField, new ColumnDescription("LONGTEXT", false, true, null, null, false));
        performCreate(map, null);
      } else {
        // Upgrade code
        ColumnDescription cd = (ColumnDescription) existing.get(groupNameField);
        if (cd == null) {
          Map addMap = new HashMap();
          addMap.put(
              groupNameField,
              new ColumnDescription(
                  "VARCHAR(32)",
                  false,
                  true,
                  authMgr.getTableName(),
                  authMgr.getGroupNameColumn(),
                  false));
          performAlter(addMap, null, null, null);
        }
        // Get rid of the authorityName field.  When we do this we need to copy into the group name
        // field, adding groups if they don't yet exist first
        cd = (ColumnDescription) existing.get(authorityNameField);
        if (cd != null) {
          ArrayList params = new ArrayList();
          IResultSet set =
              performQuery(
                  "SELECT " + nameField + "," + authorityNameField + " FROM " + getTableName(),
                  null,
                  null,
                  null);
          for (int i = 0; i < set.getRowCount(); i++) {
            IResultRow row = set.getRow(i);
            String repoName = (String) row.getValue(nameField);
            String authName = (String) row.getValue(authorityNameField);
            if (authName != null && authName.length() > 0) {
              // Attempt to create a matching auth group.  This will fail if the group
              // already exists
              IAuthorityGroup grp = authMgr.create();
              grp.setName(authName);
              try {
                authMgr.save(grp);
              } catch (ManifoldCFException e) {
                if (e.getErrorCode() == ManifoldCFException.INTERRUPTED) throw e;
                // Fall through; the row exists already
              }
              Map<String, String> map = new HashMap<String, String>();
              map.put(groupNameField, authName);
              params.clear();
              String query =
                  buildConjunctionClause(
                      params, new ClauseDescription[] {new UnitaryClause(nameField, repoName)});
              performUpdate(map, " WHERE " + query, params, null);
            }
          }
          List<String> deleteList = new ArrayList<String>();
          deleteList.add(authorityNameField);
          performAlter(null, null, deleteList, null);
        }
      }

      // Install dependent tables.
      historyManager.install(getTableName(), nameField);
      throttleSpecManager.install(getTableName(), nameField);

      // Index management
      IndexDescription authorityIndex = new IndexDescription(false, new String[] {groupNameField});
      IndexDescription classIndex = new IndexDescription(false, new String[] {classNameField});

      // Get rid of indexes that shouldn't be there
      Map indexes = getTableIndexes(null, null);
      Iterator iter = indexes.keySet().iterator();
      while (iter.hasNext()) {
        String indexName = (String) iter.next();
        IndexDescription id = (IndexDescription) indexes.get(indexName);

        if (authorityIndex != null && id.equals(authorityIndex)) authorityIndex = null;
        else if (classIndex != null && id.equals(classIndex)) classIndex = null;
        else if (indexName.indexOf("_pkey") == -1)
          // This index shouldn't be here; drop it
          performRemoveIndex(indexName);
      }

      // Add the ones we didn't find
      if (authorityIndex != null) performAddIndex(null, authorityIndex);

      if (classIndex != null) performAddIndex(null, classIndex);

      break;
    }
  }
 /**
  * Generate a report, listing the start time, elapsed time, result code and description, number of
  * bytes, and entity identifier. The records selected for this report are based on the filtering
  * criteria object passed into this method. The record order is based on the sorting criteria
  * object passed into this method. The resultset returned should have the following columns:
  * "starttime","elapsedtime","resultcode","resultdesc","bytes","identifier".
  *
  * @param connectionName is the name of the connection.
  * @param criteria is the filtering criteria, which selects the records of interest.
  * @param sort is the sorting order, which can specify sort based on the result columns.
  * @param startRow is the first row to include (beginning with 0)
  * @param maxRowCount is the maximum number of rows to include.
  */
 public IResultSet genHistorySimple(
     String connectionName, FilterCriteria criteria, SortOrder sort, int startRow, int maxRowCount)
     throws ManifoldCFException {
   return historyManager.simpleReport(connectionName, criteria, sort, startRow, maxRowCount);
 }
 /**
  * Get the maximum number of rows a window-based report can work with.
  *
  * @return the maximum rows.
  */
 public long getMaxRows() throws ManifoldCFException {
   return historyManager.getMaxRows();
 }
 /**
  * Count the number of rows specified by a given set of criteria. This can be used to make
  * decisions as to whether a query based on those rows will complete in an acceptable amount of
  * time.
  *
  * @param connectionName is the name of the connection.
  * @param criteria is the filtering criteria, which selects the records of interest.
  * @return the number of rows included by the criteria.
  */
 public long countHistoryRows(String connectionName, FilterCriteria criteria)
     throws ManifoldCFException {
   return historyManager.countHistoryRows(connectionName, criteria);
 }
 /**
  * Delete history rows older than a specified timestamp.
  *
  * @param timeCutoff is the timestamp to delete older rows before.
  */
 @Override
 public void cleanUpHistoryData(long timeCutoff) throws ManifoldCFException {
   historyManager.deleteOldRows(timeCutoff);
 }
 /**
  * Delete history rows related to a specific connection, upon user request.
  *
  * @param connectionName is the connection whose history records should be removed.
  */
 @Override
 public void cleanUpHistoryData(String connectionName) throws ManifoldCFException {
   historyManager.deleteOwner(connectionName);
 }