Package fr.becpg.repo.audit.service.impl
Class DatabaseAuditServiceImpl
java.lang.Object
fr.becpg.repo.audit.service.impl.DatabaseAuditServiceImpl
- All Implemented Interfaces:
DatabaseAuditService
DatabaseAuditServiceImpl class.
- Version:
- $Id: $Id
- Author:
- matthieu
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static final recordA closed range of audit entry identifiers.private static final recordWhat a scan gathered, and the identifiers it has not read yet.private class -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate org.alfresco.rest.api.Auditprivate org.alfresco.repo.audit.AuditComponentprivate static final StringConstantBECPG_AUDIT_PATH="/becpg/audit"private static final StringConstantID_BOUND_MAX="max"private static final StringConstantID_BOUND_MIN="min"private static final intConstantINITIAL_WINDOW_FACTOR=4private static final org.apache.commons.logging.LogConstantloggerprivate intprivate org.alfresco.service.transaction.TransactionServiceprivate static final PatternConstantUNSUPPORTED_FILTER_VALUE_CHARSprivate static final StringConstantVALUE_SUFFIX="/value"private static final intConstantWINDOW_GROWTH_FACTOR=4 -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate DatabaseAuditServiceImpl.IdRangeThe identifiers of the first and the last entry of the audit application.private StringbuildFilterStatement(DatabaseAuditPlugin plugin, String filter) buildFilterStatement.private org.alfresco.rest.framework.resource.parameters.where.QuerybuildQuery(String whereClause) buildQuery.private StringbuildWhereClause(DatabaseAuditPlugin plugin, AuditQuery auditFilter) buildWhereClause.private Comparator<org.alfresco.rest.api.model.AuditEntry> byEntryId(boolean ascending) voidcompleteAuditEntry(DatabaseAuditPlugin plugin, String filterKey, String filterValue) Complete the entry left open by an operation recorded on start, the one matching the given filter.voiddeleteAuditEntries(DatabaseAuditPlugin plugin, Long fromId, Long toId) deleteAuditEntries.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.private List<org.alfresco.rest.api.model.AuditEntry> firstEntries(List<org.alfresco.rest.api.model.AuditEntry> entries, int maxResults) intGetter for the fieldmaxScannedWindows.private List<org.alfresco.rest.api.model.AuditEntry> internalListAuditEntries(DatabaseAuditPlugin plugin, AuditQuery auditFilter) List the audit entries matching the given filter.private intinternalRecordAuditEntry(DatabaseAuditPlugin auditPlugin, Map<String, Serializable> auditValues, boolean deleteOldEntry) private booleanisScanInterrupted(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.List<org.json.JSONObject> listAuditEntries(DatabaseAuditPlugin plugin, AuditQuery auditQuery) listAuditEntries.listAuditPage(DatabaseAuditPlugin plugin, AuditQuery auditQuery) listAuditPage.private DatabaseAuditServiceImpl.IdRangenextWindow(DatabaseAuditServiceImpl.IdRange remaining, long windowSize, boolean ascending) private List<org.alfresco.rest.api.model.AuditEntry> queryAuditEntries(DatabaseAuditPlugin plugin, AuditQuery auditFilter) intrecordAuditEntry(DatabaseAuditPlugin auditPlugin, Map<String, Serializable> auditValues, boolean deleteOldEntry) recordAuditEntry.private Map<String, Serializable> recreateAuditMap(DatabaseAuditPlugin plugin, Map<String, Serializable> auditValues, boolean forDatabase) recreateAuditMap.private LongresumeId(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.private AuditPagescanAuditPage(DatabaseAuditPlugin plugin, AuditQuery auditQuery) 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.private DatabaseAuditServiceImpl.IdRangescanRange(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.scanWindows(DatabaseAuditPlugin plugin, AuditQuery auditQuery, DatabaseAuditServiceImpl.IdRange range) Walk through the identifier range, window by window, until the page is filled, the range is exhausted or the window budget is spent.voidsetMaxScannedWindows(int maxScannedWindows) Setter for the fieldmaxScannedWindows.private DatabaseAuditServiceImpl.IdRangeshrink(DatabaseAuditServiceImpl.IdRange remaining, DatabaseAuditServiceImpl.IdRange scanned, boolean ascending) private voidsortStatistics(DatabaseAuditPlugin plugin, AuditQuery auditQuery, List<org.json.JSONObject> statistics) private voidsortStatisticsByDate(List<org.json.JSONObject> statistics, String sortBy, boolean ascending) 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.private org.json.JSONObjecttoStatItem(DatabaseAuditPlugin plugin, org.alfresco.rest.api.model.AuditEntry auditEntry) private AuditQuerywindowQuery(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.
-
Field Details
-
BECPG_AUDIT_PATH
ConstantBECPG_AUDIT_PATH="/becpg/audit"- See Also:
-
VALUE_SUFFIX
ConstantVALUE_SUFFIX="/value"- See Also:
-
UNSUPPORTED_FILTER_VALUE_CHARS
ConstantUNSUPPORTED_FILTER_VALUE_CHARS -
ID_BOUND_MIN
ConstantID_BOUND_MIN="min"- See Also:
-
ID_BOUND_MAX
ConstantID_BOUND_MAX="max"- See Also:
-
INITIAL_WINDOW_FACTOR
private static final int INITIAL_WINDOW_FACTORConstantINITIAL_WINDOW_FACTOR=4- See Also:
-
WINDOW_GROWTH_FACTOR
private static final int WINDOW_GROWTH_FACTORConstantWINDOW_GROWTH_FACTOR=4- See Also:
-
logger
private static final org.apache.commons.logging.Log loggerConstantlogger -
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:
recordAuditEntryin interfaceDatabaseAuditService- Parameters:
auditPlugin- aDatabaseAuditPluginobjectauditValues- aMapobjectdeleteOldEntry- 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:
listAuditEntriesin interfaceDatabaseAuditService- Parameters:
plugin- aDatabaseAuditPluginobjectauditQuery- aAuditQueryobject- Returns:
- a
Listobject
-
listAuditPage
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:
listAuditPagein interfaceDatabaseAuditService- Parameters:
plugin- aDatabaseAuditPluginobjectauditQuery- aAuditQueryobject- Returns:
- a
AuditPageobject
-
scanAuditPage
-
scanWindows
private DatabaseAuditServiceImpl.ScanResult scanWindows(DatabaseAuditPlugin plugin, AuditQuery auditQuery, DatabaseAuditServiceImpl.IdRange range) 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- aDatabaseAuditPluginobjectauditQuery- aAuditQueryobjectrange- the identifiers left to read- Returns:
- what the scan gathered and where it stopped
-
scanFilters
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- aDatabaseAuditPluginobjectauditQuery- aAuditQueryobject- Returns:
- a
Listobject
-
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- aAuditQueryobjectfilter- the filter the window is read withwindow- the identifiers the window covers- Returns:
- a
AuditQueryobject
-
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- aDatabaseAuditPluginobjectauditQuery- aAuditQueryobject- Returns:
- a closed range of entry identifiers
-
applicationIdRange
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- aDatabaseAuditPluginobject- Returns:
- a closed range of entry identifiers
-
nextWindow
private DatabaseAuditServiceImpl.IdRange nextWindow(DatabaseAuditServiceImpl.IdRange remaining, long windowSize, boolean ascending) -
shrink
private DatabaseAuditServiceImpl.IdRange shrink(DatabaseAuditServiceImpl.IdRange remaining, DatabaseAuditServiceImpl.IdRange scanned, boolean ascending) -
byEntryId
-
firstEntries
-
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 scanauditQuery- aAuditQueryobjectpageSize- the number of entries the page holds- Returns:
- a
Longobject
-
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 scanauditQuery- aAuditQueryobjectpageSize- 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- aDatabaseAuditPluginobjectauditQuery- aAuditQueryobjectauditEntries- aCollectionobject- Returns:
- a
Listobject
-
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
-
completeAuditEntry
Complete the entry left open by an operation recorded on start, the one matching the given filter.- Specified by:
completeAuditEntryin interfaceDatabaseAuditService- Parameters:
plugin- aDatabaseAuditPluginobjectfilterKey- the audit key correlating the entry to the completed operationfilterValue- 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 byDatabaseAuditScope.- Parameters:
plugin- aDatabaseAuditPluginobjectauditEntry- aAuditEntryobject- Returns:
- a
Mapobject
-
deleteAuditEntries
deleteAuditEntries.
- Specified by:
deleteAuditEntriesin interfaceDatabaseAuditService- Parameters:
plugin- aDatabaseAuditPluginobjectfromId- aLongobjecttoId- aLongobject
-
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- aDatabaseAuditPluginobjectauditFilter- aAuditQueryobject- Returns:
- a
Listobject
-
queryAuditEntries
private List<org.alfresco.rest.api.model.AuditEntry> queryAuditEntries(DatabaseAuditPlugin plugin, AuditQuery auditFilter) -
buildWhereClause
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- aDatabaseAuditPluginobjectauditFilter- aAuditQueryobject- Returns:
- a
Stringobject
-
buildFilterStatement
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- aDatabaseAuditPluginobjectfilter- aStringobject- Returns:
- a
Stringobject - Throws:
BeCPGAuditException- if the filter syntax, key or value is not supported
-
buildQuery
buildQuery.
- Parameters:
whereClause- aStringobject- Returns:
- a
Queryobject
-
recreateAuditMap
private Map<String,Serializable> recreateAuditMap(DatabaseAuditPlugin plugin, Map<String, Serializable> auditValues, boolean forDatabase) recreateAuditMap.
- Parameters:
plugin- aDatabaseAuditPluginobjectauditValues- aMapobjectforDatabase- a boolean- Returns:
- a
Mapobject
-