Class DatabaseAuditServiceImpl

java.lang.Object
fr.becpg.repo.audit.service.impl.DatabaseAuditServiceImpl
All Implemented Interfaces:
DatabaseAuditService

@Service public class DatabaseAuditServiceImpl extends Object implements DatabaseAuditService

DatabaseAuditServiceImpl class.

Version:
$Id: $Id
Author:
matthieu
  • Field Details

    • BECPG_AUDIT_PATH

      private static final String BECPG_AUDIT_PATH
      Constant BECPG_AUDIT_PATH="/becpg/audit"
      See Also:
    • VALUE_SUFFIX

      private static final String VALUE_SUFFIX
      Constant VALUE_SUFFIX="/value"
      See Also:
    • UNSUPPORTED_FILTER_VALUE_CHARS

      private static final Pattern UNSUPPORTED_FILTER_VALUE_CHARS
      Constant UNSUPPORTED_FILTER_VALUE_CHARS
    • ID_BOUND_MIN

      private static final String ID_BOUND_MIN
      Constant ID_BOUND_MIN="min"
      See Also:
    • ID_BOUND_MAX

      private static final String ID_BOUND_MAX
      Constant ID_BOUND_MAX="max"
      See Also:
    • INITIAL_WINDOW_FACTOR

      private static final int INITIAL_WINDOW_FACTOR
      Constant INITIAL_WINDOW_FACTOR=4
      See Also:
    • WINDOW_GROWTH_FACTOR

      private static final int WINDOW_GROWTH_FACTOR
      Constant WINDOW_GROWTH_FACTOR=4
      See Also:
    • logger

      private static final org.apache.commons.logging.Log logger
      Constant logger
    • audit

      @Autowired @Qualifier("auditApi") @Lazy private org.alfresco.rest.api.Audit audit
    • auditComponent

      @Autowired private org.alfresco.repo.audit.AuditComponent auditComponent
    • transactionService

      @Autowired private org.alfresco.service.transaction.TransactionService transactionService
    • maxScannedWindows

      private int maxScannedWindows
  • Constructor Details

    • DatabaseAuditServiceImpl

      public DatabaseAuditServiceImpl()
  • Method Details

    • setMaxScannedWindows

      @Value("${becpg.audit.maxScannedWindows}") public void setMaxScannedWindows(int maxScannedWindows)

      Setter for the field maxScannedWindows.

      Parameters:
      maxScannedWindows - a int
    • getMaxScannedWindows

      public int getMaxScannedWindows()

      Getter for the field maxScannedWindows.

      Returns:
      a int
    • recordAuditEntry

      public int recordAuditEntry(DatabaseAuditPlugin auditPlugin, Map<String,Serializable> auditValues, boolean deleteOldEntry)

      recordAuditEntry.

      An entry recorded on start is written in its own transaction: the trace of an operation that may never complete cannot depend on the outcome of that very operation, and replacing it requires a writable transaction that the read only callers do not provide.
      Specified by:
      recordAuditEntry in interface DatabaseAuditService
      Parameters:
      auditPlugin - a DatabaseAuditPlugin object
      auditValues - a Map object
      deleteOldEntry - a boolean
      Returns:
      a int
    • internalRecordAuditEntry

      private int internalRecordAuditEntry(DatabaseAuditPlugin auditPlugin, Map<String,Serializable> auditValues, boolean deleteOldEntry)
    • listAuditEntries

      public List<org.json.JSONObject> listAuditEntries(DatabaseAuditPlugin plugin, AuditQuery auditQuery)

      listAuditEntries.

      Specified by:
      listAuditEntries in interface DatabaseAuditService
      Parameters:
      plugin - a DatabaseAuditPlugin object
      auditQuery - a AuditQuery object
      Returns:
      a List object
    • listAuditPage

      public AuditPage listAuditPage(DatabaseAuditPlugin plugin, AuditQuery auditQuery)

      listAuditPage.

      Read one page of audit entries by keyset paging, so that the database only ever walks through a bounded window of entry identifiers. The audit query of Alfresco carries no 'limit': whatever the requested page size, the database produces every entry of the application and the driver buffers them all. The page is therefore read by keyset paging, one window of entry identifiers after the other, which bounds each query on the index leading with the identifier and keeps the database order.
      Specified by:
      listAuditPage in interface DatabaseAuditService
      Parameters:
      plugin - a DatabaseAuditPlugin object
      auditQuery - a AuditQuery object
      Returns:
      a AuditPage object
    • scanAuditPage

      private AuditPage scanAuditPage(DatabaseAuditPlugin plugin, AuditQuery auditQuery)
    • scanWindows

      Walk through the identifier range, window by window, until the page is filled, the range is exhausted or the window budget is spent. Windows grow geometrically: an application whose entries are sparse in the identifier space, or a filter matching few entries, is reached in a logarithmic number of queries.
      Parameters:
      plugin - a DatabaseAuditPlugin object
      auditQuery - a AuditQuery object
      range - the identifiers left to read
      Returns:
      what the scan gathered and where it stopped
    • scanFilters

      private List<String> scanFilters(DatabaseAuditPlugin plugin, AuditQuery auditQuery)
      The filters a scan queries the database with: the one of the request, plus the one an extra query plugin derives from it. A plugin deriving an extra query mutates the query it is handed, hence the copy: the request has to keep its own filter for the windows still to come.
      Parameters:
      plugin - a DatabaseAuditPlugin object
      auditQuery - a AuditQuery object
      Returns:
      a List object
    • windowQuery

      private AuditQuery windowQuery(AuditQuery auditQuery, String filter, DatabaseAuditServiceImpl.IdRange window)
      The query of one window: the filter it reads with, the identifiers it is bounded to, and one entry more than the page holds. That extra entry is what tells a window the scan drained from a window it had to cut short: without it, a window holding more matches than the page could be taken for the last one and the entries it still held would be skipped by the resumed scan.
      Parameters:
      auditQuery - a AuditQuery object
      filter - the filter the window is read with
      window - the identifiers the window covers
      Returns:
      a AuditQuery object
    • scanRange

      private DatabaseAuditServiceImpl.IdRange scanRange(DatabaseAuditPlugin plugin, AuditQuery auditQuery)
      The range of entry identifiers a scan walks through: the one of the audit application, narrowed to what a resumed scan has left to read.
      Parameters:
      plugin - a DatabaseAuditPlugin object
      auditQuery - a AuditQuery object
      Returns:
      a closed range of entry identifiers
    • applicationIdRange

      private DatabaseAuditServiceImpl.IdRange applicationIdRange(DatabaseAuditPlugin plugin)
      The identifiers of the first and the last entry of the audit application. The database resolves both from the index without reading a single row, so a scan can afford to read them on every request.
      Parameters:
      plugin - a DatabaseAuditPlugin object
      Returns:
      a closed range of entry identifiers
    • nextWindow

      private DatabaseAuditServiceImpl.IdRange nextWindow(DatabaseAuditServiceImpl.IdRange remaining, long windowSize, boolean ascending)
    • shrink

    • byEntryId

      private Comparator<org.alfresco.rest.api.model.AuditEntry> byEntryId(boolean ascending)
    • firstEntries

      private List<org.alfresco.rest.api.model.AuditEntry> firstEntries(List<org.alfresco.rest.api.model.AuditEntry> entries, int maxResults)
    • resumeId

      private Long resumeId(DatabaseAuditServiceImpl.ScanResult scan, AuditQuery auditQuery, int pageSize)
      The identifier the next page starts after: the last entry of this page when the scan left matching entries behind it, the last identifier it read when it stopped earlier, and none when it reached the end of the audit application.
      Parameters:
      scan - the outcome of the scan
      auditQuery - a AuditQuery object
      pageSize - the number of entries the page holds
      Returns:
      a Long object
    • isScanInterrupted

      private boolean isScanInterrupted(DatabaseAuditServiceImpl.ScanResult scan, AuditQuery auditQuery, int pageSize)
      True when the scan spent its window budget without filling the page: the caller has to resume it to know whether further entries match.
      Parameters:
      scan - the outcome of the scan
      auditQuery - a AuditQuery object
      pageSize - the number of entries the page holds
      Returns:
      a boolean
    • toStatistics

      private List<org.json.JSONObject> toStatistics(DatabaseAuditPlugin plugin, AuditQuery auditQuery, Collection<org.alfresco.rest.api.model.AuditEntry> auditEntries)
      Turn audit entries into the statistics the callers expose, ordered on the requested column. The order is applied in memory, hence on the read entries only: the audit values a business column holds live in the property tables, which the audit query cannot sort on.
      Parameters:
      plugin - a DatabaseAuditPlugin object
      auditQuery - a AuditQuery object
      auditEntries - a Collection object
      Returns:
      a List object
    • toStatItem

      private org.json.JSONObject toStatItem(DatabaseAuditPlugin plugin, org.alfresco.rest.api.model.AuditEntry auditEntry)
    • sortStatistics

      private void sortStatistics(DatabaseAuditPlugin plugin, AuditQuery auditQuery, List<org.json.JSONObject> statistics)
    • sortStatisticsByDate

      private void sortStatisticsByDate(List<org.json.JSONObject> statistics, String sortBy, boolean ascending)
    • completeAuditEntry

      public void completeAuditEntry(DatabaseAuditPlugin plugin, String filterKey, String filterValue)
      Complete the entry left open by an operation recorded on start, the one matching the given filter.
      Specified by:
      completeAuditEntry in interface DatabaseAuditService
      Parameters:
      plugin - a DatabaseAuditPlugin object
      filterKey - the audit key correlating the entry to the completed operation
      filterValue - the value of that key
    • extractAuditValues

      private Map<String,Serializable> extractAuditValues(DatabaseAuditPlugin plugin, org.alfresco.rest.api.model.AuditEntry auditEntry)
      Read back the values of an audit entry, keyed by audit key. The identifier is read as an integer: it is the key the entry is replaced on, and it is recorded as an integer by DatabaseAuditScope.
      Parameters:
      plugin - a DatabaseAuditPlugin object
      auditEntry - a AuditEntry object
      Returns:
      a Map object
    • deleteAuditEntries

      public void deleteAuditEntries(DatabaseAuditPlugin plugin, Long fromId, Long toId)

      deleteAuditEntries.

      Specified by:
      deleteAuditEntries in interface DatabaseAuditService
      Parameters:
      plugin - a DatabaseAuditPlugin object
      fromId - a Long object
      toId - a Long object
    • internalListAuditEntries

      private List<org.alfresco.rest.api.model.AuditEntry> internalListAuditEntries(DatabaseAuditPlugin plugin, AuditQuery auditFilter)
      List the audit entries matching the given filter. Read as system: the Alfresco audit service is reserved to the administrators, whereas the entries are read back on behalf of the user having requested the audited operation.
      Parameters:
      plugin - a DatabaseAuditPlugin object
      auditFilter - a AuditQuery object
      Returns:
      a List object
    • queryAuditEntries

      private List<org.alfresco.rest.api.model.AuditEntry> queryAuditEntries(DatabaseAuditPlugin plugin, AuditQuery auditFilter)
    • buildWhereClause

      private String buildWhereClause(DatabaseAuditPlugin plugin, AuditQuery auditFilter)

      buildWhereClause.

      The identifier range is written as an inclusive 'between': the walker of Alfresco raises the upper bound by one to turn it into the exclusive bound the query needs.
      Parameters:
      plugin - a DatabaseAuditPlugin object
      auditFilter - a AuditQuery object
      Returns:
      a String object
    • buildFilterStatement

      private String buildFilterStatement(DatabaseAuditPlugin plugin, String filter)

      buildFilterStatement.

      Builds the 'valuesKey'/'valuesValue' predicate of the audit 'where' clause. A value carrying a character that the clause cannot represent is rejected rather than escaped: the 'where' grammar does accept escape sequences, but the query is read back with QueryHelper.stripQuotes, which only removes the surrounding quotes. An escaped value would therefore be searched with its backslashes and silently match nothing.
      Parameters:
      plugin - a DatabaseAuditPlugin object
      filter - a String object
      Returns:
      a String object
      Throws:
      BeCPGAuditException - if the filter syntax, key or value is not supported
    • buildQuery

      private org.alfresco.rest.framework.resource.parameters.where.Query buildQuery(String whereClause)

      buildQuery.

      Parameters:
      whereClause - a String object
      Returns:
      a Query object
    • recreateAuditMap

      private Map<String,Serializable> recreateAuditMap(DatabaseAuditPlugin plugin, Map<String,Serializable> auditValues, boolean forDatabase)

      recreateAuditMap.

      Parameters:
      plugin - a DatabaseAuditPlugin object
      auditValues - a Map object
      forDatabase - a boolean
      Returns:
      a Map object