From 131f6e67da682cfee3ba59b03e04a17342789b48 Mon Sep 17 00:00:00 2001 From: Sathish Kumar Date: Thu, 12 Dec 2024 09:08:17 +0530 Subject: [PATCH] fix for the apps-3129 --- ...Mv33HoldAuditEntryValuesPatchUnitTest.java | 12 +- .../org/alfresco/rest/api/impl/AuditImpl.java | 7 +- .../alfresco/repo/audit/AuditComponent.java | 573 ++--- .../repo/audit/AuditComponentImpl.java | 1924 ++++++++--------- .../alfresco/repo/audit/AuditServiceImpl.java | 392 ++-- .../org/alfresco/repo/domain/CrcHelper.java | 243 ++- .../alfresco/repo/domain/audit/AuditDAO.java | 515 ++--- .../domain/audit/ibatis/AuditDAOImpl.java | 631 +++--- .../propval/PropertyStringValueEntity.java | 332 ++- .../service/cmr/audit/AuditService.java | 533 ++--- 10 files changed, 2574 insertions(+), 2588 deletions(-) diff --git a/amps/ags/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/patch/v33/RMv33HoldAuditEntryValuesPatchUnitTest.java b/amps/ags/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/patch/v33/RMv33HoldAuditEntryValuesPatchUnitTest.java index e89f055e45..af065295e4 100644 --- a/amps/ags/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/patch/v33/RMv33HoldAuditEntryValuesPatchUnitTest.java +++ b/amps/ags/rm-community/rm-community-repo/unit-test/java/org/alfresco/module/org_alfresco_module_rm/patch/v33/RMv33HoldAuditEntryValuesPatchUnitTest.java @@ -93,15 +93,15 @@ public class RMv33HoldAuditEntryValuesPatchUnitTest verify(mockedRecordsManagementQueryDAO, times(1)).updatePropertyStringValueEntity(deleteHoldPropertyStringValueEntity); assertEquals("Add To Hold", addToHoldPropertyStringValueEntity.getStringValue()); - assertEquals("add to hold", addToHoldPropertyStringValueEntity.getStringLower()); + assertEquals("add to hold", addToHoldPropertyStringValueEntity.getStringEndLower()); assertEquals(Long.valueOf(770_786_109L), addToHoldPropertyStringValueEntity.getStringCrc()); - assertEquals("Remove From Hold", removeFromHoldPropertyStringValueEntity.getStringValue()); - assertEquals("remove from hold", removeFromHoldPropertyStringValueEntity.getStringLower()); + assertEquals("Remove From Hold", removeFromHoldPropertyStringValueEntity.getStrRvingValue()); + assertEquals("remove from hold", removeFromHoldPropertyStringValueEntity.getStringEndLower()); assertEquals(Long.valueOf(2_967_613_012L), removeFromHoldPropertyStringValueEntity.getStringCrc()); assertEquals("Delete Hold", deleteHoldPropertyStringValueEntity.getStringValue()); - assertEquals("delete hold", deleteHoldPropertyStringValueEntity.getStringLower()); + assertEquals("delete hold", deleteHoldPropertyStringValueEntity.getStringEndLower()); assertEquals(Long.valueOf(132_640_810L), deleteHoldPropertyStringValueEntity.getStringCrc()); } @@ -122,6 +122,4 @@ public class RMv33HoldAuditEntryValuesPatchUnitTest verify(mockedRecordsManagementQueryDAO, times(1)).getPropertyStringValueEntity("deleteHold"); verify(mockedRecordsManagementQueryDAO, times(0)).updatePropertyStringValueEntity(any()); } -} - - +} \ No newline at end of file diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/AuditImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/AuditImpl.java index d2259e3c6c..c7c895b539 100644 --- a/remote-api/src/main/java/org/alfresco/rest/api/impl/AuditImpl.java +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/AuditImpl.java @@ -909,14 +909,15 @@ public class AuditImpl implements Audit public int getAuditEntriesCountByAppAndProperties(AuditService.AuditApplication auditApplication, AuditEntryQueryWalker propertyWalker) { + final String applicationName = auditApplication.getKey().substring(1); AuditQueryParameters parameters = new AuditQueryParameters(); - parameters.setApplicationName(auditApplication.getName()); + parameters.setApplicationName(applicationName); parameters.setFromTime(propertyWalker.getFromTime()); parameters.setToTime(propertyWalker.getToTime()); parameters.setFromId(propertyWalker.getFromId()); parameters.setToId(propertyWalker.getToId()); parameters.setUser(propertyWalker.getCreatedByUser()); - return auditService.getAuditEntriesCountByAppAndProperties(parameters); + return auditService.getAuditEntriesCountByAppAndProperties(applicationName, parameters); } -} +} \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/audit/AuditComponent.java b/repository/src/main/java/org/alfresco/repo/audit/AuditComponent.java index 29955432a7..3193ea7e90 100644 --- a/repository/src/main/java/org/alfresco/repo/audit/AuditComponent.java +++ b/repository/src/main/java/org/alfresco/repo/audit/AuditComponent.java @@ -1,286 +1,287 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2024 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.repo.audit; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.alfresco.repo.audit.model.AuditApplication; -import org.alfresco.repo.audit.model.AuditModelRegistry; -import org.alfresco.repo.audit.model._3.AuditPath; -import org.alfresco.service.cmr.audit.AuditQueryParameters; -import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback; - -/** - * The audit component. Used by the AuditService and AuditMethodInterceptor to insert audit entries. - * - * @author Derek Hulley - */ -public interface AuditComponent -{ - /** - * Determines whether audit is globally enabled or disabled. - * - * @return Returns true if audit is enabled - * - * @since 3.3 - */ - public boolean isAuditEnabled(); - - /** - * Switch auditing on or off - * - * @param enable true to enable auditing or false to disable - * - * @since 3.4 - */ - public void setAuditEnabled(boolean enable); - - /** - * @param userAuditFilter UserAuditFilter - * - * @since 4.2 - */ - public void setUserAuditFilter(UserAuditFilter userAuditFilter); - - /** - * Get all registered audit applications, whether active or not. - * - * @return Returns a map of registered audit applications keyed by name - * - * @since 3.4 - */ - public Map getAuditApplications(); - - /** - * Determine whether the audit infrastructure expects audit values to be passed in. - * This is a helper method to allow optimizations in the client code. Reasons why - * this method might return false are: auditing is disabled; no audit applications - * have been registered. Sometimes, depending on the log level, this method may always - * return true. - * - * - * @return Returns true if the calling code (data producers) - * should go ahead and generate the data for - * {@link #recordAuditValues(String, Map) recording}. - * - * @since 3.3 - */ - public boolean areAuditValuesRequired(); - - /** - * Determines if audit values are required based on whether there are any audit applications - * registered to record data for the given path. This helper method gives data producers a - * shortcut in the event that nothing would be recorded in any event. - * - * @param path the audit path - * @return Returns true if there is at least one audit application - * registered to handle the given path. - * - * @since 3.4 - */ - public boolean areAuditValuesRequired(String path); - - /** - * Delete audit entries for the given application and time range - * - * @param applicationName the name of the application - * @param fromTime the start time of entries to remove (inclusive and optional) - * @param toTime the end time of entries to remove (exclusive and optional) - * @return Returns the number of entries deleted - * - * @since 3.2 - */ - int deleteAuditEntries(String applicationName, Long fromTime, Long toTime); - - /** - * Delete audit entries for the given application and id range - * - * @param applicationName the name of the application - * @param fromId the start time of entries to remove (inclusive and optional) - * @param toId the end time of entries to remove (exclusive and optional) - * @return Returns the number of entries deleted - * - * @since 5.2.2 - */ - int deleteAuditEntriesByIdRange(String applicationName, Long fromId, Long toId); - - /** - * Delete a discrete list of audit entries based on ID - * - * @param auditEntryIds the audit entry IDs to delete - * @return Returns the number of entries deleted - */ - int deleteAuditEntries(List auditEntryIds); - - /** - * Check if an audit path is enabled. The path will be disabled if it or any higher - * path has been explicitly disabled. Any disabled path will not be processed when - * data is audited. - * - * @param applicationName the name of the application being logged to - * @param path the audit path to check or null to assume the - * application's root path - * @return Returns true if the audit path has been disabled - * - * @since 3.2 - */ - boolean isAuditPathEnabled(String applicationName, String path); - - /** - * Enable auditing (if it is not already enabled) for all paths that contain the given path. - * The path is the path as originally logged and - * not the path that the generated data may contain - although this would be similarly - * enabled. - *

- * If the enabled - * - * @param applicationName the name of the application being logged to - * @param path the audit path to check or null to assume the - * application's root path - * - * @since 3.2 - */ - void enableAudit(String applicationName, String path); - - /** - * Disable auditing (if it is not already disabled) for all paths that contain the given path. - * The path is the path as originally logged and - * not the path that the generated data may contain - although this would be similarly - * disabled. - *

- * If the path is /x/y then any data paths that start with /x/y will be stripped - * out before data generators and data recorders are applied. If the path represents - * the root path of the application, then auditing for that application is effectively disabled. - * - * @param applicationName the name of the application being logged to - * @param path the audit path to check or null to assume the - * application's root path - * - * @since 3.2 - */ - void disableAudit(String applicationName, String path); - - /** - * Remove all disabled paths i.e. enable all per-path based auditing. Auditing may still be - * disabled globally. This is primarily for test purposes; applications should know which - * paths need {@link #enableAudit(String, String) enabling} or - * {@link #disableAudit(String, String) disabled}. - * - * @param applicationName the name of the application - * - * @since 3.2 - */ - void resetDisabledPaths(String applicationName); - - /** - * Create an audit entry for the given map of values. The map key is a path - starting with '/' - * ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) - relative to the root path provided. - *

- * The root path and value keys are combined to produce a map of data keyed by full path. This - * fully-pathed map is then passed through the - * {@link AuditModelRegistry#getAuditPathMapper() audit path mapper}. The result may yield data - * destined for several different - * {@link AuditModelRegistry#getAuditApplicationByKey(String) audit applications}. depending on - * the data extraction and generation defined in the applications, values (or derived values) may - * be recorded against several audit entries (one per application represented). - *

- * The return values reflect what was actually persisted and is controlled by the data extractors - * defined in the audit configuration. - *

- * A new read-write transaction is started if there are values to write that there is not a viable - * transaction present. - * - * @param rootPath a base path of {@link AuditPath} key entries concatenated with the path separator - * '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) - * @param values the values to audit mapped by {@link AuditPath} key relative to root path - * (may be null) - * @return Returns the values that were actually persisted, keyed by their full path. - * @throws IllegalStateException if the transaction state could not be determined - * - * @since 3.2 - */ - Map recordAuditValues(String rootPath, Map values); - - /** - * The same as {@link AuditComponent#recordAuditValues(String, Map)}, but with controlled usage of userFilter - * - * @param rootPath a base path of {@link AuditPath} key entries concatenated with the path separator - * '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) - * @param values the values to audit mapped by {@link AuditPath} key relative to root path - * (may be null) - * @param useUserFilter if false the user filter is disabled. - * @return Returns the values that were actually persisted, keyed by their full path. - * @throws IllegalStateException if the transaction state could not be determined - */ - Map recordAuditValuesWithUserFilter(String rootPath, Map values, boolean useUserFilter); - - /** - * Find audit entries using the given parameters - * - * @param callback the data callback per entry - * @param parameters the parameters for the query (may not be null) - * @param maxResults the maximum number of results to retrieve (must be greater than 0) - * - * @throws IllegalArgumentException if maxResults less or equal to zero - * - * @since 3.2 - */ - void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults); - - /** - * Issue an audit query to retrieve min / max audit record id for a given application. - * - * @param applicationName the name of the application - * @param extremes a list containing min/max or both - * @return a map containing min/max and the associated value - */ - HashMap getAuditMinMaxByApp(String applicationName, List extremes); - - /** - * Issue an audit query to retrieve count of records for a given application. - * - * @param applicationName the name of the application - * @return a map containing min/max and the associated value - */ - default int getAuditEntriesCountByApp(String applicationName) - { - return -1; - } - - /** - * Issue an audit query to retrieve count of records for a given application and properties - * - * @param parameters audit parameters provided by the where clause on the ReST API - * @return a map containing min/max and the associated value - */ - default int getAuditEntriesCountByAppAndProperties(AuditQueryParameters parameters) - { - return -1; - } -} +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.audit; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.audit.model.AuditApplication; +import org.alfresco.repo.audit.model.AuditModelRegistry; +import org.alfresco.repo.audit.model._3.AuditPath; +import org.alfresco.service.cmr.audit.AuditQueryParameters; +import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback; + +/** + * The audit component. Used by the AuditService and AuditMethodInterceptor to insert audit entries. + * + * @author Derek Hulley + */ +public interface AuditComponent +{ + /** + * Determines whether audit is globally enabled or disabled. + * + * @return Returns true if audit is enabled + * + * @since 3.3 + */ + public boolean isAuditEnabled(); + + /** + * Switch auditing on or off + * + * @param enable true to enable auditing or false to disable + * + * @since 3.4 + */ + public void setAuditEnabled(boolean enable); + + /** + * @param userAuditFilter UserAuditFilter + * + * @since 4.2 + */ + public void setUserAuditFilter(UserAuditFilter userAuditFilter); + + /** + * Get all registered audit applications, whether active or not. + * + * @return Returns a map of registered audit applications keyed by name + * + * @since 3.4 + */ + public Map getAuditApplications(); + + /** + * Determine whether the audit infrastructure expects audit values to be passed in. + * This is a helper method to allow optimizations in the client code. Reasons why + * this method might return false are: auditing is disabled; no audit applications + * have been registered. Sometimes, depending on the log level, this method may always + * return true. + * + * + * @return Returns true if the calling code (data producers) + * should go ahead and generate the data for + * {@link #recordAuditValues(String, Map) recording}. + * + * @since 3.3 + */ + public boolean areAuditValuesRequired(); + + /** + * Determines if audit values are required based on whether there are any audit applications + * registered to record data for the given path. This helper method gives data producers a + * shortcut in the event that nothing would be recorded in any event. + * + * @param path the audit path + * @return Returns true if there is at least one audit application + * registered to handle the given path. + * + * @since 3.4 + */ + public boolean areAuditValuesRequired(String path); + + /** + * Delete audit entries for the given application and time range + * + * @param applicationName the name of the application + * @param fromTime the start time of entries to remove (inclusive and optional) + * @param toTime the end time of entries to remove (exclusive and optional) + * @return Returns the number of entries deleted + * + * @since 3.2 + */ + int deleteAuditEntries(String applicationName, Long fromTime, Long toTime); + + /** + * Delete audit entries for the given application and id range + * + * @param applicationName the name of the application + * @param fromId the start time of entries to remove (inclusive and optional) + * @param toId the end time of entries to remove (exclusive and optional) + * @return Returns the number of entries deleted + * + * @since 5.2.2 + */ + int deleteAuditEntriesByIdRange(String applicationName, Long fromId, Long toId); + + /** + * Delete a discrete list of audit entries based on ID + * + * @param auditEntryIds the audit entry IDs to delete + * @return Returns the number of entries deleted + */ + int deleteAuditEntries(List auditEntryIds); + + /** + * Check if an audit path is enabled. The path will be disabled if it or any higher + * path has been explicitly disabled. Any disabled path will not be processed when + * data is audited. + * + * @param applicationName the name of the application being logged to + * @param path the audit path to check or null to assume the + * application's root path + * @return Returns true if the audit path has been disabled + * + * @since 3.2 + */ + boolean isAuditPathEnabled(String applicationName, String path); + + /** + * Enable auditing (if it is not already enabled) for all paths that contain the given path. + * The path is the path as originally logged and + * not the path that the generated data may contain - although this would be similarly + * enabled. + *

+ * If the enabled + * + * @param applicationName the name of the application being logged to + * @param path the audit path to check or null to assume the + * application's root path + * + * @since 3.2 + */ + void enableAudit(String applicationName, String path); + + /** + * Disable auditing (if it is not already disabled) for all paths that contain the given path. + * The path is the path as originally logged and + * not the path that the generated data may contain - although this would be similarly + * disabled. + *

+ * If the path is /x/y then any data paths that start with /x/y will be stripped + * out before data generators and data recorders are applied. If the path represents + * the root path of the application, then auditing for that application is effectively disabled. + * + * @param applicationName the name of the application being logged to + * @param path the audit path to check or null to assume the + * application's root path + * + * @since 3.2 + */ + void disableAudit(String applicationName, String path); + + /** + * Remove all disabled paths i.e. enable all per-path based auditing. Auditing may still be + * disabled globally. This is primarily for test purposes; applications should know which + * paths need {@link #enableAudit(String, String) enabling} or + * {@link #disableAudit(String, String) disabled}. + * + * @param applicationName the name of the application + * + * @since 3.2 + */ + void resetDisabledPaths(String applicationName); + + /** + * Create an audit entry for the given map of values. The map key is a path - starting with '/' + * ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) - relative to the root path provided. + *

+ * The root path and value keys are combined to produce a map of data keyed by full path. This + * fully-pathed map is then passed through the + * {@link AuditModelRegistry#getAuditPathMapper() audit path mapper}. The result may yield data + * destined for several different + * {@link AuditModelRegistry#getAuditApplicationByKey(String) audit applications}. depending on + * the data extraction and generation defined in the applications, values (or derived values) may + * be recorded against several audit entries (one per application represented). + *

+ * The return values reflect what was actually persisted and is controlled by the data extractors + * defined in the audit configuration. + *

+ * A new read-write transaction is started if there are values to write that there is not a viable + * transaction present. + * + * @param rootPath a base path of {@link AuditPath} key entries concatenated with the path separator + * '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) + * @param values the values to audit mapped by {@link AuditPath} key relative to root path + * (may be null) + * @return Returns the values that were actually persisted, keyed by their full path. + * @throws IllegalStateException if the transaction state could not be determined + * + * @since 3.2 + */ + Map recordAuditValues(String rootPath, Map values); + + /** + * The same as {@link AuditComponent#recordAuditValues(String, Map)}, but with controlled usage of userFilter + * + * @param rootPath a base path of {@link AuditPath} key entries concatenated with the path separator + * '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) + * @param values the values to audit mapped by {@link AuditPath} key relative to root path + * (may be null) + * @param useUserFilter if false the user filter is disabled. + * @return Returns the values that were actually persisted, keyed by their full path. + * @throws IllegalStateException if the transaction state could not be determined + */ + Map recordAuditValuesWithUserFilter(String rootPath, Map values, boolean useUserFilter); + + /** + * Find audit entries using the given parameters + * + * @param callback the data callback per entry + * @param parameters the parameters for the query (may not be null) + * @param maxResults the maximum number of results to retrieve (must be greater than 0) + * + * @throws IllegalArgumentException if maxResults less or equal to zero + * + * @since 3.2 + */ + void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults); + + /** + * Issue an audit query to retrieve min / max audit record id for a given application. + * + * @param applicationName the name of the application + * @param extremes a list containing min/max or both + * @return a map containing min/max and the associated value + */ + HashMap getAuditMinMaxByApp(String applicationName, List extremes); + + /** + * Issue an audit query to retrieve count of records for a given application. + * + * @param applicationName the name of the application + * @return a map containing min/max and the associated value + */ + default int getAuditEntriesCountByApp(String applicationName) + { + return -1; + } + + /** + * Issue an audit query to retrieve count of records for a given application and properties + * + * @param applicationName the name of the application + * @param parameters audit parameters provided by the where clause on the ReST API + * @return a map containing min/max and the associated value + */ + default int getAuditEntriesCountByAppAndProperties(String applicationName, AuditQueryParameters parameters) + { + return -1; + } +} \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/audit/AuditComponentImpl.java b/repository/src/main/java/org/alfresco/repo/audit/AuditComponentImpl.java index 98479db3af..784d96679e 100644 --- a/repository/src/main/java/org/alfresco/repo/audit/AuditComponentImpl.java +++ b/repository/src/main/java/org/alfresco/repo/audit/AuditComponentImpl.java @@ -1,963 +1,963 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2024 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.repo.audit; - -import java.io.Serializable; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.audit.extractor.DataExtractor; -import org.alfresco.repo.audit.generator.DataGenerator; -import org.alfresco.repo.audit.model.AuditApplication; -import org.alfresco.repo.audit.model.AuditModelRegistry; -import org.alfresco.repo.audit.model.AuditModelRegistryImpl; -import org.alfresco.repo.audit.model.AuditApplication.DataExtractorDefinition; -import org.alfresco.repo.domain.audit.AuditDAO; -import org.alfresco.repo.domain.propval.PropertyValueDAO; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.cmr.audit.AuditQueryParameters; -import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.PathMapper; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.extensions.surf.util.ParameterCheck; - -/** - * Component that records audit values as well as providing the query implementation. - *

- * To turn on logging of all potentially auditable data, turn on logging for:
- * {@link #INBOUND_LOGGER org.alfresco.repo.audit.inbound}. - *

- * TODO: Respect audit internal - at the moment audit internal is fixed to false. - * - * @author Derek Hulley - * @since 3.2 (in its current form) - */ -public class AuditComponentImpl implements AuditComponent -{ - private static final String INBOUND_LOGGER = "org.alfresco.repo.audit.inbound"; - - private static Log logger = LogFactory.getLog(AuditComponentImpl.class); - private static Log loggerInbound = LogFactory.getLog(INBOUND_LOGGER); - - private AuditModelRegistryImpl auditModelRegistry; - private PropertyValueDAO propertyValueDAO; - private AuditDAO auditDAO; - private TransactionService transactionService; - private AuditFilter auditFilter; - private UserAuditFilter userAuditFilter; - - /** - * Default constructor - */ - public AuditComponentImpl() - { - } - - /** - * Set the registry holding the audit models - * @since 3.2 - */ - public void setAuditModelRegistry(AuditModelRegistryImpl auditModelRegistry) - { - this.auditModelRegistry = auditModelRegistry; - } - - /** - * Set the DAO for manipulating property values - * @since 3.2 - */ - public void setPropertyValueDAO(PropertyValueDAO propertyValueDAO) - { - this.propertyValueDAO = propertyValueDAO; - } - - /** - * Set the DAO for accessing audit data - * @since 3.2 - */ - public void setAuditDAO(AuditDAO auditDAO) - { - this.auditDAO = auditDAO; - } - - /** - * Set the service used to start new transactions - */ - public void setTransactionService(TransactionService transactionService) - { - this.transactionService = transactionService; - } - - /** - * Set the component used to filter which audit events to record - */ - public void setAuditFilter(AuditFilter auditFilter) - { - this.auditFilter = auditFilter; - } - - public void setUserAuditFilter(UserAuditFilter userAuditFilter) - { - this.userAuditFilter = userAuditFilter; - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public int deleteAuditEntries(String applicationName, Long fromTime, Long toTime) - { - Long applicationId = getApplicationId(applicationName); - if (applicationId == null) - { - return 0; - } - - int deleted = auditDAO.deleteAuditEntries(applicationId, fromTime, toTime); - // Done - if (logger.isDebugEnabled()) - { - logger.debug( - "Delete audit " + deleted + " entries for " + applicationName + - " (" + fromTime + " to " + toTime); - } - return deleted; - } - - /** - * {@inheritDoc} - * @since 5.2.2 - */ - public int deleteAuditEntriesByIdRange(String applicationName, Long fromId, Long toId) - { - Long applicationId = getApplicationId(applicationName); - if (applicationId == null) - { - return 0; - } - - int deleted = auditDAO.deleteAuditEntriesByIdRange(applicationId, fromId, toId); - // Done - if (logger.isDebugEnabled()) - { - logger.debug( - "Delete audit " + deleted + " entries for " + applicationName + - " (" + fromId + " to " + toId); - } - return deleted; - } - - private Long getApplicationId(String applicationName) - { - ParameterCheck.mandatory("applicationName", applicationName); - AlfrescoTransactionSupport.checkTransactionReadState(true); - - AuditApplication application = auditModelRegistry.getAuditApplicationByName(applicationName); - if (application == null) - { - if (logger.isDebugEnabled()) - { - logger.debug("No audit application named '" + applicationName + "' has been registered."); - } - return null; - } - - return application.getApplicationId(); - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - @Override - public int deleteAuditEntries(List auditEntryIds) - { - // Shortcut, if necessary - if (auditEntryIds.size() == 0) - { - return 0; - } - return auditDAO.deleteAuditEntries(auditEntryIds); - } - - /** - * @param application the audit application object - * @return Returns a copy of the set of disabled paths associated with the application - */ - @SuppressWarnings("unchecked") - private Set getDisabledPaths(AuditApplication application) - { - try - { - Long disabledPathsId = application.getDisabledPathsId(); - Set disabledPaths = (Set) propertyValueDAO.getPropertyById(disabledPathsId); - return new HashSet(disabledPaths); - } - catch (Throwable e) - { - // Might be an invalid ID, somehow - auditModelRegistry.loadAuditModels(); - throw new AlfrescoRuntimeException("Unabled to get AuditApplication disabled paths: " + application, e); - } - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public boolean isAuditEnabled() - { - return auditModelRegistry.isAuditEnabled(); - } - - /** - * {@inheritDoc} - * @since 3.4 - */ - @Override - public void setAuditEnabled(boolean enable) - { - boolean alreadyEnabled = auditModelRegistry.isAuditEnabled(); - if (alreadyEnabled != enable) - { - // It is changing - auditModelRegistry.stop(); - auditModelRegistry.setProperty( - AuditModelRegistry.AUDIT_PROPERTY_AUDIT_ENABLED, - Boolean.toString(enable).toLowerCase()); - auditModelRegistry.start(); - } - } - - /** - * {@inheritDoc} - * @since 3.4 - */ - public Map getAuditApplications() - { - return auditModelRegistry.getAuditApplications(); - } - - /** - * {@inheritDoc} - *

- * Note that if DEBUG is on for the the {@link #INBOUND_LOGGER}, then true - * will always be returned. - * - * @since 3.2 - */ - public boolean areAuditValuesRequired() - { - return (loggerInbound.isDebugEnabled()) || - (isAuditEnabled() && !auditModelRegistry.getAuditPathMapper().isEmpty()); - } - - /** - * {@inheritDoc} - *

- * Note that if DEBUG is on for the the {@link #INBOUND_LOGGER}, then true - * will always be returned. - * - * @since 3.4 - */ - @Override - public boolean areAuditValuesRequired(String path) - { - PathMapper pathMapper = auditModelRegistry.getAuditPathMapper(); - Set mappedPaths = pathMapper.getMappedPathsWithPartialMatch(path); - return loggerInbound.isDebugEnabled() || mappedPaths.size() > 0; - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public boolean isAuditPathEnabled(String applicationName, String path) - { - ParameterCheck.mandatory("applicationName", applicationName); - AlfrescoTransactionSupport.checkTransactionReadState(false); - - AuditApplication application = auditModelRegistry.getAuditApplicationByName(applicationName); - if (application == null) - { - if (logger.isDebugEnabled()) - { - logger.debug("No audit application named '" + applicationName + "' has been registered."); - } - return false; - } - // Ensure that the path gets a valid value - if (path == null) - { - path = AuditApplication.AUDIT_PATH_SEPARATOR + application.getApplicationKey(); - } - else - { - // Check the path against the application - application.checkPath(path); - } - - Set disabledPaths = getDisabledPaths(application); - - // Check if there are any entries that match or supercede the given path - String disablingPath = null;; - for (String disabledPath : disabledPaths) - { - if (path.startsWith(disabledPath)) - { - disablingPath = disabledPath; - break; - } - } - // Done - if (logger.isDebugEnabled()) - { - logger.debug( - "Audit path enabled check: \n" + - " Application: " + applicationName + "\n" + - " Path: " + path + "\n" + - " Disabling Path: " + disablingPath); - } - return disablingPath == null; - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public void enableAudit(String applicationName, String path) - { - ParameterCheck.mandatory("applicationName", applicationName); - AlfrescoTransactionSupport.checkTransactionReadState(true); - - AuditApplication application = auditModelRegistry.getAuditApplicationByName(applicationName); - if (application == null) - { - if (logger.isDebugEnabled()) - { - logger.debug("No audit application named '" + applicationName + "' has been registered."); - } - return; - } - // Ensure that the path gets a valid value - if (path == null) - { - path = AuditApplication.AUDIT_PATH_SEPARATOR + application.getApplicationKey(); - } - else - { - // Check the path against the application - application.checkPath(path); - } - - Long disabledPathsId = application.getDisabledPathsId(); - Set disabledPaths = getDisabledPaths(application); - - // Remove any paths that start with the given path - boolean changed = false; - Iterator iterateDisabledPaths = disabledPaths.iterator(); - while (iterateDisabledPaths.hasNext()) - { - String disabledPath = iterateDisabledPaths.next(); - if (disabledPath.startsWith(path)) - { - iterateDisabledPaths.remove(); - changed = true; - } - } - // Persist, if necessary - if (changed) - { - propertyValueDAO.updateProperty(disabledPathsId, (Serializable) disabledPaths); - if (logger.isDebugEnabled()) - { - logger.debug( - "Audit disabled paths updated: \n" + - " Application: " + applicationName + "\n" + - " Disabled: " + disabledPaths); - } - } - // Done - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public void disableAudit(String applicationName, String path) - { - ParameterCheck.mandatory("applicationName", applicationName); - AlfrescoTransactionSupport.checkTransactionReadState(true); - - AuditApplication application = auditModelRegistry.getAuditApplicationByName(applicationName); - if (application == null) - { - if (logger.isDebugEnabled()) - { - logger.debug("No audit application named '" + applicationName + "' has been registered."); - } - return; - } - // Ensure that the path gets a valid value - if (path == null) - { - path = AuditApplication.AUDIT_PATH_SEPARATOR + application.getApplicationKey(); - } - else - { - // Check the path against the application - application.checkPath(path); - } - - Long disabledPathsId = application.getDisabledPathsId(); - Set disabledPaths = getDisabledPaths(application); - - // Shortcut if the disabled paths contain the exact path - if (disabledPaths.contains(path)) - { - if (logger.isDebugEnabled()) - { - logger.debug( - "Audit disable path already present: \n" + - " Path: " + path); - } - return; - } - - // Bring the set up to date by stripping out unwanted paths - Iterator iterateDisabledPaths = disabledPaths.iterator(); - while (iterateDisabledPaths.hasNext()) - { - String disabledPath = iterateDisabledPaths.next(); - if (disabledPath.startsWith(path)) - { - // We will be superceding this - iterateDisabledPaths.remove(); - } - else if (path.startsWith(disabledPath)) - { - // There is already a superceding path - if (logger.isDebugEnabled()) - { - logger.debug( - "Audit disable path superceded: \n" + - " Path: " + path + "\n" + - " Superceded by: " + disabledPath); - } - return; - } - } - // Add our path in - disabledPaths.add(path); - // Upload the new set - propertyValueDAO.updateProperty(disabledPathsId, (Serializable) disabledPaths); - // Done - if (logger.isDebugEnabled()) - { - logger.debug( - "Audit disabled paths updated: \n" + - " Application: " + applicationName + "\n" + - " Disabled: " + disabledPaths); - } - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public void resetDisabledPaths(String applicationName) - { - ParameterCheck.mandatory("applicationName", applicationName); - AlfrescoTransactionSupport.checkTransactionReadState(true); - - AuditApplication application = auditModelRegistry.getAuditApplicationByName(applicationName); - if (application == null) - { - if (logger.isDebugEnabled()) - { - logger.debug("No audit application named '" + applicationName + "' has been registered."); - } - return; - } - Long disabledPathsId = application.getDisabledPathsId(); - propertyValueDAO.updateProperty(disabledPathsId, (Serializable) Collections.emptySet()); - // Done - if (logger.isDebugEnabled()) - { - logger.debug("Removed all disabled paths for application " + applicationName); - } - } - - @Override - public Map recordAuditValues(String rootPath, Map values) - { - return recordAuditValuesWithUserFilter(rootPath, values, true); - } - - @Override - public Map recordAuditValuesWithUserFilter(String rootPath, Map values, boolean useUserFilter) - { - ParameterCheck.mandatory("rootPath", rootPath); - AuditApplication.checkPathFormat(rootPath); - - String username = AuthenticationUtil.getFullyAuthenticatedUser(); - if (values == null || values.isEmpty() || !areAuditValuesRequired() - || !(userAuditFilter.acceptUser(username) || !useUserFilter) || !auditFilter.accept(rootPath, values)) - { - return Collections.emptyMap(); - } - - // Log inbound values - if (loggerInbound.isDebugEnabled()) - { - StringBuilder sb = new StringBuilder(values.size()*64); - sb.append("\n") - .append("Inbound audit values:"); - for (Map.Entry entry : values.entrySet()) - { - String pathElement = entry.getKey(); - String path = AuditApplication.buildPath(rootPath, pathElement); - Serializable value = entry.getValue(); - sb.append("\n\t").append(path).append("=").append(value); - } - loggerInbound.debug(sb.toString()); - } - - // Build the key paths using the session root path - Map pathedValues = new HashMap(values.size() * 2); - for (Map.Entry entry : values.entrySet()) - { - String pathElement = entry.getKey(); - String path = AuditApplication.buildPath(rootPath, pathElement); - pathedValues.put(path, entry.getValue()); - } - - // Translate the values map - PathMapper pathMapper = auditModelRegistry.getAuditPathMapper(); - final Map mappedValues = pathMapper.convertMap(pathedValues); - if (mappedValues.isEmpty()) - { - return mappedValues; - } - - // We have something to record. Start a transaction, if necessary - TxnReadState txnState = AlfrescoTransactionSupport.getTransactionReadState(); - switch (txnState) - { - case TXN_NONE: - case TXN_READ_ONLY: - // New transaction - RetryingTransactionCallback> callback = - new RetryingTransactionCallback>() - { - public Map execute() throws Throwable - { - return recordAuditValuesImpl(mappedValues); - } - }; - RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); - txnHelper.setForceWritable(true); - return txnHelper.doInTransaction(callback, false, true); - case TXN_READ_WRITE: - return recordAuditValuesImpl(mappedValues); - default: - throw new IllegalStateException("Unknown txn state: " + txnState); - } - } - - /** - * @since 3.2 - */ - public Map recordAuditValuesImpl(Map mappedValues) - { - // Group the values by root path - Map> mappedValuesByRootKey = new HashMap>(); - for (Map.Entry entry : mappedValues.entrySet()) - { - String path = entry.getKey(); - String rootKey = AuditApplication.getRootKey(path); - Map rootKeyMappedValues = mappedValuesByRootKey.get(rootKey); - if (rootKeyMappedValues == null) - { - rootKeyMappedValues = new HashMap(7); - mappedValuesByRootKey.put(rootKey, rootKeyMappedValues); - } - rootKeyMappedValues.put(path, entry.getValue()); - } - - Map allAuditedValues = new HashMap(mappedValues.size()*2+1); - // Now audit for each of the root keys - for (Map.Entry> entry : mappedValuesByRootKey.entrySet()) - { - String rootKey = entry.getKey(); - Map rootKeyMappedValues = entry.getValue(); - // Get the application - AuditApplication application = auditModelRegistry.getAuditApplicationByKey(rootKey); - if (application == null) - { - // There is no application that uses the root key - logger.debug( - "There is no application for root key: " + rootKey); - continue; - } - // Get the disabled paths - Set disabledPaths = getDisabledPaths(application); - // Do a quick elimination if the root path is disabled - if (disabledPaths.contains(AuditApplication.buildPath(rootKey))) - { - // The root key has been disabled for this application - if (logger.isDebugEnabled()) - { - logger.debug( - "Audit values root path has been excluded by disabled paths: \n" + - " Application: " + application + "\n" + - " Root Path: " + AuditApplication.buildPath(rootKey)); - } - continue; - } - // Do the audit - Map rootKeyAuditValues = audit(application, disabledPaths, rootKeyMappedValues); - allAuditedValues.putAll(rootKeyAuditValues); - } - // Done - return allAuditedValues; - } - - /** - * Audit values for a given application. No path checking is done. - * - * @param application the audit application to audit to - * @param disabledPaths the application's disabled paths - * @param values the values to store keyed by full paths. - * @return Returns all values as audited - */ - private Map audit( - final AuditApplication application, - Set disabledPaths, - final Map values) - { - // Get the model ID for the application - Long applicationId = application.getApplicationId(); - if (applicationId == null) - { - throw new AuditException("No persisted instance exists for audit application: " + application); - } - - // Check if there is anything to audit - if (values.size() == 0) - { - if (logger.isDebugEnabled()) - { - logger.debug( - "Audit values have all been excluded by disabled paths: \n" + - " Application: " + application + "\n" + - " Values: " + values); - } - return Collections.emptyMap(); - } - - Set generatorKeys = values.keySet(); - // Eliminate any paths that have been disabled - Iterator generatorKeysIterator = generatorKeys.iterator(); - while(generatorKeysIterator.hasNext()) - { - String generatorKey = generatorKeysIterator.next(); - for (String disabledPath : disabledPaths) - { - if (generatorKey.startsWith(disabledPath)) - { - // The pathed value is excluded - generatorKeysIterator.remove(); - } - } - } - - // Generate data - Map generators = application.getDataGenerators(generatorKeys); - Map auditData = generateData(generators); - - // Now extract values - Map extractedData = AuthenticationUtil.runAs(new RunAsWork>() - { - public Map doWork() throws Exception - { - return extractData(application, values); - } - }, AuthenticationUtil.getSystemUserName()); - - // Combine extracted and generated values (extracted data takes precedence) - auditData.putAll(extractedData); - - // Filter data - // See MNT-14136, MNT-8401 - for (Map.Entry value : auditData.entrySet()) - { - String root = value.getKey(); - int index = root.lastIndexOf("/"); - Map argc = new HashMap(1); - argc.put(root.substring(index, root.length()).substring(1), value.getValue()); - if (!auditFilter.accept(root.substring(0, index), argc)) - { - return Collections.emptyMap(); - } - } - - // Time and username are intrinsic - long time = System.currentTimeMillis(); - String username = AuthenticationUtil.getFullyAuthenticatedUser(); - - Long entryId = null; - if (!auditData.isEmpty()) - { - // Persist the values (if not just gathering data in a pre call for use in a post call) - boolean justGatherPreCallData = application.isApplicationJustGeneratingPreCallData(); - if (!justGatherPreCallData) - { - entryId = auditDAO.createAuditEntry(applicationId, time, username, auditData); - } - // Done - if (logger.isDebugEnabled()) - { - StringBuilder sb = new StringBuilder(); - sb.append( - ((justGatherPreCallData) ? "\nPreCallData: \n" : "\nNew audit entry: \n") + - "\tApplication ID: " + applicationId + "\n" + - ((justGatherPreCallData) ? "" : "\tEntry ID: " + entryId + "\n") + - "\tValues: " + "\n"); - for (Map.Entry entry : values.entrySet()) - { - sb.append("\t\t").append(entry).append("\n"); - } - sb.append("\n\tAudit Data: \n"); - for (Map.Entry entry : auditData.entrySet()) - { - sb.append("\t\t").append(entry).append("\n"); - } - logger.debug(sb.toString()); - } - } - else - { - // Done ... nothing - if (logger.isDebugEnabled()) - { - StringBuilder sb = new StringBuilder(); - sb.append( - "\nNothing audited: \n" + - "\tApplication ID: " + applicationId + "\n" + - "\tEntry ID: " + entryId + "\n" + - "\tValues: " + "\n"); - for (Map.Entry entry : values.entrySet()) - { - sb.append("\t\t").append(entry).append("\n"); - } - logger.debug(sb.toString()); - } - } - - return auditData; - } - - /** - * Extracts data from a given map using data extractors from the given application. - * - * @param application the application providing the data extractors - * @param values the data values from which to generate data - * @return Returns a map of derived data keyed by full path - * - * @since 3.2 - */ - private Map extractData( - AuditApplication application, - Map values) - { - Map newData = new HashMap(values.size()); - - List extractors = application.getDataExtractors(); - for (DataExtractorDefinition extractorDef : extractors) - { - DataExtractor extractor = extractorDef.getDataExtractor(); - String triggerPath = extractorDef.getDataTrigger(); - String sourcePath = extractorDef.getDataSource(); - String targetPath = extractorDef.getDataTarget(); - - // Check if it is triggered - if (!values.containsKey(triggerPath)) - { - continue; // It is not triggered - } - - // We observe the key, not the actual value - if (!values.containsKey(sourcePath)) - { - continue; // There is no data to extract - } - - Serializable value = values.get(sourcePath); - - // Check if the extraction is supported - if (!extractor.isSupported(value)) - { - continue; - } - // Use the extractor to pull the value out - final Serializable data; - try - { - data = extractor.extractData(value); - } - catch (Throwable e) - { - throw new AlfrescoRuntimeException( - "Failed to extract audit data: \n" + - " Path: " + sourcePath + "\n" + - " Raw value: " + value + "\n" + - " Extractor: " + extractor, - e); - } - // Add it to the map - newData.put(targetPath, data); - } - // Done - if (logger.isDebugEnabled()) - { - StringBuilder sb = new StringBuilder(); - sb.append( - "\nExtracted audit data: \n" + - "\tApplication: " + application + "\n" + - "\tValues: " + "\n"); - for (Map.Entry entry : values.entrySet()) - { - sb.append("\t\t").append(entry).append("\n"); - } - sb.append("\n\tNew Data: \n"); - for (Map.Entry entry : newData.entrySet()) - { - sb.append("\t\t").append(entry).append("\n"); - } - logger.debug(sb.toString()); - } - return newData; - } - - /** - * @param generators the data generators - * @return Returns a map of generated data keyed by full path - * - * @since 3.2 - */ - private Map generateData(Map generators) - { - Map newData = new HashMap(generators.size() + 5); - for (Map.Entry entry : generators.entrySet()) - { - String path = entry.getKey(); - DataGenerator generator = entry.getValue(); - final Serializable data; - try - { - data = generator.getData(); - } - catch (Throwable e) - { - throw new AlfrescoRuntimeException( - "Failed to generate audit data: \n" + - " Path: " + path + "\n" + - " Generator: " + generator, - e); - } - // Add it to the map - newData.put(path, data); - } - // Done - return newData; - } - - /** - * {@inheritDoc} - */ - public void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults) - { - ParameterCheck.mandatory("callback", callback); - ParameterCheck.mandatory("parameters", parameters); - - // Shortcuts - if (parameters.isZeroResultQuery()) - { - return; - } - - auditDAO.findAuditEntries(callback, parameters, maxResults); - } - - /** - * {@inheritDoc} - */ - public HashMap getAuditMinMaxByApp(String applicationName, List extremes) - { - // Get the id for the application - AuditApplication app = auditModelRegistry.getAuditApplicationByName(applicationName); - Long applicationId = app.getApplicationId(); - if (applicationId == null) - { - throw new AuditException("No persisted instance exists for audit application: " + app); - } - - return auditDAO.getAuditMinMaxByApp(applicationId, extremes); - } - - @Override - public int getAuditEntriesCountByApp(String applicationName) - { - // Get the id for the application - AuditApplication app = auditModelRegistry.getAuditApplicationByName(applicationName); - Long applicationId = app.getApplicationId(); - if (applicationId == null) - { - throw new AuditException("No persisted instance exists for audit application: " + app); - } - - return auditDAO.getAuditEntriesCountByApp(applicationId); - } - - @Override public int getAuditEntriesCountByAppAndProperties(AuditQueryParameters parameters) - { - return auditDAO.getAuditEntriesCountByAppAndProperties(parameters); - } +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.audit; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.audit.extractor.DataExtractor; +import org.alfresco.repo.audit.generator.DataGenerator; +import org.alfresco.repo.audit.model.AuditApplication; +import org.alfresco.repo.audit.model.AuditModelRegistry; +import org.alfresco.repo.audit.model.AuditModelRegistryImpl; +import org.alfresco.repo.audit.model.AuditApplication.DataExtractorDefinition; +import org.alfresco.repo.domain.audit.AuditDAO; +import org.alfresco.repo.domain.propval.PropertyValueDAO; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.audit.AuditQueryParameters; +import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.PathMapper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.ParameterCheck; + +/** + * Component that records audit values as well as providing the query implementation. + *

+ * To turn on logging of all potentially auditable data, turn on logging for:
+ * {@link #INBOUND_LOGGER org.alfresco.repo.audit.inbound}. + *

+ * TODO: Respect audit internal - at the moment audit internal is fixed to false. + * + * @author Derek Hulley + * @since 3.2 (in its current form) + */ +public class AuditComponentImpl implements AuditComponent +{ + private static final String INBOUND_LOGGER = "org.alfresco.repo.audit.inbound"; + + private static Log logger = LogFactory.getLog(AuditComponentImpl.class); + private static Log loggerInbound = LogFactory.getLog(INBOUND_LOGGER); + + private AuditModelRegistryImpl auditModelRegistry; + private PropertyValueDAO propertyValueDAO; + private AuditDAO auditDAO; + private TransactionService transactionService; + private AuditFilter auditFilter; + private UserAuditFilter userAuditFilter; + + /** + * Default constructor + */ + public AuditComponentImpl() + { + } + + /** + * Set the registry holding the audit models + * @since 3.2 + */ + public void setAuditModelRegistry(AuditModelRegistryImpl auditModelRegistry) + { + this.auditModelRegistry = auditModelRegistry; + } + + /** + * Set the DAO for manipulating property values + * @since 3.2 + */ + public void setPropertyValueDAO(PropertyValueDAO propertyValueDAO) + { + this.propertyValueDAO = propertyValueDAO; + } + + /** + * Set the DAO for accessing audit data + * @since 3.2 + */ + public void setAuditDAO(AuditDAO auditDAO) + { + this.auditDAO = auditDAO; + } + + /** + * Set the service used to start new transactions + */ + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + /** + * Set the component used to filter which audit events to record + */ + public void setAuditFilter(AuditFilter auditFilter) + { + this.auditFilter = auditFilter; + } + + public void setUserAuditFilter(UserAuditFilter userAuditFilter) + { + this.userAuditFilter = userAuditFilter; + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public int deleteAuditEntries(String applicationName, Long fromTime, Long toTime) + { + Long applicationId = getApplicationId(applicationName); + if (applicationId == null) + { + return 0; + } + + int deleted = auditDAO.deleteAuditEntries(applicationId, fromTime, toTime); + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Delete audit " + deleted + " entries for " + applicationName + + " (" + fromTime + " to " + toTime); + } + return deleted; + } + + /** + * {@inheritDoc} + * @since 5.2.2 + */ + public int deleteAuditEntriesByIdRange(String applicationName, Long fromId, Long toId) + { + Long applicationId = getApplicationId(applicationName); + if (applicationId == null) + { + return 0; + } + + int deleted = auditDAO.deleteAuditEntriesByIdRange(applicationId, fromId, toId); + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Delete audit " + deleted + " entries for " + applicationName + + " (" + fromId + " to " + toId); + } + return deleted; + } + + private Long getApplicationId(String applicationName) + { + ParameterCheck.mandatory("applicationName", applicationName); + AlfrescoTransactionSupport.checkTransactionReadState(true); + + AuditApplication application = auditModelRegistry.getAuditApplicationByName(applicationName); + if (application == null) + { + if (logger.isDebugEnabled()) + { + logger.debug("No audit application named '" + applicationName + "' has been registered."); + } + return null; + } + + return application.getApplicationId(); + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + @Override + public int deleteAuditEntries(List auditEntryIds) + { + // Shortcut, if necessary + if (auditEntryIds.size() == 0) + { + return 0; + } + return auditDAO.deleteAuditEntries(auditEntryIds); + } + + /** + * @param application the audit application object + * @return Returns a copy of the set of disabled paths associated with the application + */ + @SuppressWarnings("unchecked") + private Set getDisabledPaths(AuditApplication application) + { + try + { + Long disabledPathsId = application.getDisabledPathsId(); + Set disabledPaths = (Set) propertyValueDAO.getPropertyById(disabledPathsId); + return new HashSet(disabledPaths); + } + catch (Throwable e) + { + // Might be an invalid ID, somehow + auditModelRegistry.loadAuditModels(); + throw new AlfrescoRuntimeException("Unabled to get AuditApplication disabled paths: " + application, e); + } + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public boolean isAuditEnabled() + { + return auditModelRegistry.isAuditEnabled(); + } + + /** + * {@inheritDoc} + * @since 3.4 + */ + @Override + public void setAuditEnabled(boolean enable) + { + boolean alreadyEnabled = auditModelRegistry.isAuditEnabled(); + if (alreadyEnabled != enable) + { + // It is changing + auditModelRegistry.stop(); + auditModelRegistry.setProperty( + AuditModelRegistry.AUDIT_PROPERTY_AUDIT_ENABLED, + Boolean.toString(enable).toLowerCase()); + auditModelRegistry.start(); + } + } + + /** + * {@inheritDoc} + * @since 3.4 + */ + public Map getAuditApplications() + { + return auditModelRegistry.getAuditApplications(); + } + + /** + * {@inheritDoc} + *

+ * Note that if DEBUG is on for the the {@link #INBOUND_LOGGER}, then true + * will always be returned. + * + * @since 3.2 + */ + public boolean areAuditValuesRequired() + { + return (loggerInbound.isDebugEnabled()) || + (isAuditEnabled() && !auditModelRegistry.getAuditPathMapper().isEmpty()); + } + + /** + * {@inheritDoc} + *

+ * Note that if DEBUG is on for the the {@link #INBOUND_LOGGER}, then true + * will always be returned. + * + * @since 3.4 + */ + @Override + public boolean areAuditValuesRequired(String path) + { + PathMapper pathMapper = auditModelRegistry.getAuditPathMapper(); + Set mappedPaths = pathMapper.getMappedPathsWithPartialMatch(path); + return loggerInbound.isDebugEnabled() || mappedPaths.size() > 0; + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public boolean isAuditPathEnabled(String applicationName, String path) + { + ParameterCheck.mandatory("applicationName", applicationName); + AlfrescoTransactionSupport.checkTransactionReadState(false); + + AuditApplication application = auditModelRegistry.getAuditApplicationByName(applicationName); + if (application == null) + { + if (logger.isDebugEnabled()) + { + logger.debug("No audit application named '" + applicationName + "' has been registered."); + } + return false; + } + // Ensure that the path gets a valid value + if (path == null) + { + path = AuditApplication.AUDIT_PATH_SEPARATOR + application.getApplicationKey(); + } + else + { + // Check the path against the application + application.checkPath(path); + } + + Set disabledPaths = getDisabledPaths(application); + + // Check if there are any entries that match or supercede the given path + String disablingPath = null;; + for (String disabledPath : disabledPaths) + { + if (path.startsWith(disabledPath)) + { + disablingPath = disabledPath; + break; + } + } + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Audit path enabled check: \n" + + " Application: " + applicationName + "\n" + + " Path: " + path + "\n" + + " Disabling Path: " + disablingPath); + } + return disablingPath == null; + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public void enableAudit(String applicationName, String path) + { + ParameterCheck.mandatory("applicationName", applicationName); + AlfrescoTransactionSupport.checkTransactionReadState(true); + + AuditApplication application = auditModelRegistry.getAuditApplicationByName(applicationName); + if (application == null) + { + if (logger.isDebugEnabled()) + { + logger.debug("No audit application named '" + applicationName + "' has been registered."); + } + return; + } + // Ensure that the path gets a valid value + if (path == null) + { + path = AuditApplication.AUDIT_PATH_SEPARATOR + application.getApplicationKey(); + } + else + { + // Check the path against the application + application.checkPath(path); + } + + Long disabledPathsId = application.getDisabledPathsId(); + Set disabledPaths = getDisabledPaths(application); + + // Remove any paths that start with the given path + boolean changed = false; + Iterator iterateDisabledPaths = disabledPaths.iterator(); + while (iterateDisabledPaths.hasNext()) + { + String disabledPath = iterateDisabledPaths.next(); + if (disabledPath.startsWith(path)) + { + iterateDisabledPaths.remove(); + changed = true; + } + } + // Persist, if necessary + if (changed) + { + propertyValueDAO.updateProperty(disabledPathsId, (Serializable) disabledPaths); + if (logger.isDebugEnabled()) + { + logger.debug( + "Audit disabled paths updated: \n" + + " Application: " + applicationName + "\n" + + " Disabled: " + disabledPaths); + } + } + // Done + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public void disableAudit(String applicationName, String path) + { + ParameterCheck.mandatory("applicationName", applicationName); + AlfrescoTransactionSupport.checkTransactionReadState(true); + + AuditApplication application = auditModelRegistry.getAuditApplicationByName(applicationName); + if (application == null) + { + if (logger.isDebugEnabled()) + { + logger.debug("No audit application named '" + applicationName + "' has been registered."); + } + return; + } + // Ensure that the path gets a valid value + if (path == null) + { + path = AuditApplication.AUDIT_PATH_SEPARATOR + application.getApplicationKey(); + } + else + { + // Check the path against the application + application.checkPath(path); + } + + Long disabledPathsId = application.getDisabledPathsId(); + Set disabledPaths = getDisabledPaths(application); + + // Shortcut if the disabled paths contain the exact path + if (disabledPaths.contains(path)) + { + if (logger.isDebugEnabled()) + { + logger.debug( + "Audit disable path already present: \n" + + " Path: " + path); + } + return; + } + + // Bring the set up to date by stripping out unwanted paths + Iterator iterateDisabledPaths = disabledPaths.iterator(); + while (iterateDisabledPaths.hasNext()) + { + String disabledPath = iterateDisabledPaths.next(); + if (disabledPath.startsWith(path)) + { + // We will be superceding this + iterateDisabledPaths.remove(); + } + else if (path.startsWith(disabledPath)) + { + // There is already a superceding path + if (logger.isDebugEnabled()) + { + logger.debug( + "Audit disable path superceded: \n" + + " Path: " + path + "\n" + + " Superceded by: " + disabledPath); + } + return; + } + } + // Add our path in + disabledPaths.add(path); + // Upload the new set + propertyValueDAO.updateProperty(disabledPathsId, (Serializable) disabledPaths); + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Audit disabled paths updated: \n" + + " Application: " + applicationName + "\n" + + " Disabled: " + disabledPaths); + } + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public void resetDisabledPaths(String applicationName) + { + ParameterCheck.mandatory("applicationName", applicationName); + AlfrescoTransactionSupport.checkTransactionReadState(true); + + AuditApplication application = auditModelRegistry.getAuditApplicationByName(applicationName); + if (application == null) + { + if (logger.isDebugEnabled()) + { + logger.debug("No audit application named '" + applicationName + "' has been registered."); + } + return; + } + Long disabledPathsId = application.getDisabledPathsId(); + propertyValueDAO.updateProperty(disabledPathsId, (Serializable) Collections.emptySet()); + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Removed all disabled paths for application " + applicationName); + } + } + + @Override + public Map recordAuditValues(String rootPath, Map values) + { + return recordAuditValuesWithUserFilter(rootPath, values, true); + } + + @Override + public Map recordAuditValuesWithUserFilter(String rootPath, Map values, boolean useUserFilter) + { + ParameterCheck.mandatory("rootPath", rootPath); + AuditApplication.checkPathFormat(rootPath); + + String username = AuthenticationUtil.getFullyAuthenticatedUser(); + if (values == null || values.isEmpty() || !areAuditValuesRequired() + || !(userAuditFilter.acceptUser(username) || !useUserFilter) || !auditFilter.accept(rootPath, values)) + { + return Collections.emptyMap(); + } + + // Log inbound values + if (loggerInbound.isDebugEnabled()) + { + StringBuilder sb = new StringBuilder(values.size()*64); + sb.append("\n") + .append("Inbound audit values:"); + for (Map.Entry entry : values.entrySet()) + { + String pathElement = entry.getKey(); + String path = AuditApplication.buildPath(rootPath, pathElement); + Serializable value = entry.getValue(); + sb.append("\n\t").append(path).append("=").append(value); + } + loggerInbound.debug(sb.toString()); + } + + // Build the key paths using the session root path + Map pathedValues = new HashMap(values.size() * 2); + for (Map.Entry entry : values.entrySet()) + { + String pathElement = entry.getKey(); + String path = AuditApplication.buildPath(rootPath, pathElement); + pathedValues.put(path, entry.getValue()); + } + + // Translate the values map + PathMapper pathMapper = auditModelRegistry.getAuditPathMapper(); + final Map mappedValues = pathMapper.convertMap(pathedValues); + if (mappedValues.isEmpty()) + { + return mappedValues; + } + + // We have something to record. Start a transaction, if necessary + TxnReadState txnState = AlfrescoTransactionSupport.getTransactionReadState(); + switch (txnState) + { + case TXN_NONE: + case TXN_READ_ONLY: + // New transaction + RetryingTransactionCallback> callback = + new RetryingTransactionCallback>() + { + public Map execute() throws Throwable + { + return recordAuditValuesImpl(mappedValues); + } + }; + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + txnHelper.setForceWritable(true); + return txnHelper.doInTransaction(callback, false, true); + case TXN_READ_WRITE: + return recordAuditValuesImpl(mappedValues); + default: + throw new IllegalStateException("Unknown txn state: " + txnState); + } + } + + /** + * @since 3.2 + */ + public Map recordAuditValuesImpl(Map mappedValues) + { + // Group the values by root path + Map> mappedValuesByRootKey = new HashMap>(); + for (Map.Entry entry : mappedValues.entrySet()) + { + String path = entry.getKey(); + String rootKey = AuditApplication.getRootKey(path); + Map rootKeyMappedValues = mappedValuesByRootKey.get(rootKey); + if (rootKeyMappedValues == null) + { + rootKeyMappedValues = new HashMap(7); + mappedValuesByRootKey.put(rootKey, rootKeyMappedValues); + } + rootKeyMappedValues.put(path, entry.getValue()); + } + + Map allAuditedValues = new HashMap(mappedValues.size()*2+1); + // Now audit for each of the root keys + for (Map.Entry> entry : mappedValuesByRootKey.entrySet()) + { + String rootKey = entry.getKey(); + Map rootKeyMappedValues = entry.getValue(); + // Get the application + AuditApplication application = auditModelRegistry.getAuditApplicationByKey(rootKey); + if (application == null) + { + // There is no application that uses the root key + logger.debug( + "There is no application for root key: " + rootKey); + continue; + } + // Get the disabled paths + Set disabledPaths = getDisabledPaths(application); + // Do a quick elimination if the root path is disabled + if (disabledPaths.contains(AuditApplication.buildPath(rootKey))) + { + // The root key has been disabled for this application + if (logger.isDebugEnabled()) + { + logger.debug( + "Audit values root path has been excluded by disabled paths: \n" + + " Application: " + application + "\n" + + " Root Path: " + AuditApplication.buildPath(rootKey)); + } + continue; + } + // Do the audit + Map rootKeyAuditValues = audit(application, disabledPaths, rootKeyMappedValues); + allAuditedValues.putAll(rootKeyAuditValues); + } + // Done + return allAuditedValues; + } + + /** + * Audit values for a given application. No path checking is done. + * + * @param application the audit application to audit to + * @param disabledPaths the application's disabled paths + * @param values the values to store keyed by full paths. + * @return Returns all values as audited + */ + private Map audit( + final AuditApplication application, + Set disabledPaths, + final Map values) + { + // Get the model ID for the application + Long applicationId = application.getApplicationId(); + if (applicationId == null) + { + throw new AuditException("No persisted instance exists for audit application: " + application); + } + + // Check if there is anything to audit + if (values.size() == 0) + { + if (logger.isDebugEnabled()) + { + logger.debug( + "Audit values have all been excluded by disabled paths: \n" + + " Application: " + application + "\n" + + " Values: " + values); + } + return Collections.emptyMap(); + } + + Set generatorKeys = values.keySet(); + // Eliminate any paths that have been disabled + Iterator generatorKeysIterator = generatorKeys.iterator(); + while(generatorKeysIterator.hasNext()) + { + String generatorKey = generatorKeysIterator.next(); + for (String disabledPath : disabledPaths) + { + if (generatorKey.startsWith(disabledPath)) + { + // The pathed value is excluded + generatorKeysIterator.remove(); + } + } + } + + // Generate data + Map generators = application.getDataGenerators(generatorKeys); + Map auditData = generateData(generators); + + // Now extract values + Map extractedData = AuthenticationUtil.runAs(new RunAsWork>() + { + public Map doWork() throws Exception + { + return extractData(application, values); + } + }, AuthenticationUtil.getSystemUserName()); + + // Combine extracted and generated values (extracted data takes precedence) + auditData.putAll(extractedData); + + // Filter data + // See MNT-14136, MNT-8401 + for (Map.Entry value : auditData.entrySet()) + { + String root = value.getKey(); + int index = root.lastIndexOf("/"); + Map argc = new HashMap(1); + argc.put(root.substring(index, root.length()).substring(1), value.getValue()); + if (!auditFilter.accept(root.substring(0, index), argc)) + { + return Collections.emptyMap(); + } + } + + // Time and username are intrinsic + long time = System.currentTimeMillis(); + String username = AuthenticationUtil.getFullyAuthenticatedUser(); + + Long entryId = null; + if (!auditData.isEmpty()) + { + // Persist the values (if not just gathering data in a pre call for use in a post call) + boolean justGatherPreCallData = application.isApplicationJustGeneratingPreCallData(); + if (!justGatherPreCallData) + { + entryId = auditDAO.createAuditEntry(applicationId, time, username, auditData); + } + // Done + if (logger.isDebugEnabled()) + { + StringBuilder sb = new StringBuilder(); + sb.append( + ((justGatherPreCallData) ? "\nPreCallData: \n" : "\nNew audit entry: \n") + + "\tApplication ID: " + applicationId + "\n" + + ((justGatherPreCallData) ? "" : "\tEntry ID: " + entryId + "\n") + + "\tValues: " + "\n"); + for (Map.Entry entry : values.entrySet()) + { + sb.append("\t\t").append(entry).append("\n"); + } + sb.append("\n\tAudit Data: \n"); + for (Map.Entry entry : auditData.entrySet()) + { + sb.append("\t\t").append(entry).append("\n"); + } + logger.debug(sb.toString()); + } + } + else + { + // Done ... nothing + if (logger.isDebugEnabled()) + { + StringBuilder sb = new StringBuilder(); + sb.append( + "\nNothing audited: \n" + + "\tApplication ID: " + applicationId + "\n" + + "\tEntry ID: " + entryId + "\n" + + "\tValues: " + "\n"); + for (Map.Entry entry : values.entrySet()) + { + sb.append("\t\t").append(entry).append("\n"); + } + logger.debug(sb.toString()); + } + } + + return auditData; + } + + /** + * Extracts data from a given map using data extractors from the given application. + * + * @param application the application providing the data extractors + * @param values the data values from which to generate data + * @return Returns a map of derived data keyed by full path + * + * @since 3.2 + */ + private Map extractData( + AuditApplication application, + Map values) + { + Map newData = new HashMap(values.size()); + + List extractors = application.getDataExtractors(); + for (DataExtractorDefinition extractorDef : extractors) + { + DataExtractor extractor = extractorDef.getDataExtractor(); + String triggerPath = extractorDef.getDataTrigger(); + String sourcePath = extractorDef.getDataSource(); + String targetPath = extractorDef.getDataTarget(); + + // Check if it is triggered + if (!values.containsKey(triggerPath)) + { + continue; // It is not triggered + } + + // We observe the key, not the actual value + if (!values.containsKey(sourcePath)) + { + continue; // There is no data to extract + } + + Serializable value = values.get(sourcePath); + + // Check if the extraction is supported + if (!extractor.isSupported(value)) + { + continue; + } + // Use the extractor to pull the value out + final Serializable data; + try + { + data = extractor.extractData(value); + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException( + "Failed to extract audit data: \n" + + " Path: " + sourcePath + "\n" + + " Raw value: " + value + "\n" + + " Extractor: " + extractor, + e); + } + // Add it to the map + newData.put(targetPath, data); + } + // Done + if (logger.isDebugEnabled()) + { + StringBuilder sb = new StringBuilder(); + sb.append( + "\nExtracted audit data: \n" + + "\tApplication: " + application + "\n" + + "\tValues: " + "\n"); + for (Map.Entry entry : values.entrySet()) + { + sb.append("\t\t").append(entry).append("\n"); + } + sb.append("\n\tNew Data: \n"); + for (Map.Entry entry : newData.entrySet()) + { + sb.append("\t\t").append(entry).append("\n"); + } + logger.debug(sb.toString()); + } + return newData; + } + + /** + * @param generators the data generators + * @return Returns a map of generated data keyed by full path + * + * @since 3.2 + */ + private Map generateData(Map generators) + { + Map newData = new HashMap(generators.size() + 5); + for (Map.Entry entry : generators.entrySet()) + { + String path = entry.getKey(); + DataGenerator generator = entry.getValue(); + final Serializable data; + try + { + data = generator.getData(); + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException( + "Failed to generate audit data: \n" + + " Path: " + path + "\n" + + " Generator: " + generator, + e); + } + // Add it to the map + newData.put(path, data); + } + // Done + return newData; + } + + /** + * {@inheritDoc} + */ + public void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults) + { + ParameterCheck.mandatory("callback", callback); + ParameterCheck.mandatory("parameters", parameters); + + // Shortcuts + if (parameters.isZeroResultQuery()) + { + return; + } + + auditDAO.findAuditEntries(callback, parameters, maxResults); + } + + /** + * {@inheritDoc} + */ + public HashMap getAuditMinMaxByApp(String applicationName, List extremes) + { + // Get the id for the application + AuditApplication app = auditModelRegistry.getAuditApplicationByName(applicationName); + Long applicationId = app.getApplicationId(); + if (applicationId == null) + { + throw new AuditException("No persisted instance exists for audit application: " + app); + } + + return auditDAO.getAuditMinMaxByApp(applicationId, extremes); + } + + @Override + public int getAuditEntriesCountByApp(String applicationName) + { + // Get the id for the application + AuditApplication app = auditModelRegistry.getAuditApplicationByKey(applicationName); + Long applicationId = app.getApplicationId(); + if (applicationId == null) + { + throw new AuditException("No persisted instance exists for audit application: " + app); + } + + return auditDAO.getAuditEntriesCountByApp(applicationId); + } + + @Override public int getAuditEntriesCountByAppAndProperties(String applicationName, AuditQueryParameters parameters) + { + return auditDAO.getAuditEntriesCountByAppAndProperties(applicationName, parameters); + } } \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/audit/AuditServiceImpl.java b/repository/src/main/java/org/alfresco/repo/audit/AuditServiceImpl.java index 4824914e51..0e38a64b36 100644 --- a/repository/src/main/java/org/alfresco/repo/audit/AuditServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/audit/AuditServiceImpl.java @@ -1,197 +1,197 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2024 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.repo.audit; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import org.alfresco.service.cmr.audit.AuditQueryParameters; -import org.alfresco.service.cmr.audit.AuditService; - -/** - * The implementation of the AuditService for application auditing. - * - * @author Derek Hulley - * @since 3.2 - */ -public class AuditServiceImpl implements AuditService -{ - private AuditComponent auditComponent; - - public AuditServiceImpl() - { - super(); - } - - public void setAuditComponent(AuditComponent auditComponent) - { - this.auditComponent = auditComponent; - } - - /** - * {@inheritDoc} - * @since 3.4 - */ - public boolean isAuditEnabled() - { - return auditComponent.isAuditEnabled(); - } - - /** - * {@inheritDoc} - * @since 3.4 - */ - @Override - public void setAuditEnabled(boolean enable) - { - auditComponent.setAuditEnabled(enable); - } - - /** - * {@inheritDoc} - * @since 3.4 - */ - @Override - public Map getAuditApplications() - { - Map apps = auditComponent.getAuditApplications(); - - Map ret = new TreeMap(); - for (String app : apps.keySet()) - { - String name = app; - String key = org.alfresco.repo.audit.model.AuditApplication.AUDIT_PATH_SEPARATOR + apps.get(app).getApplicationKey(); - boolean enabled = auditComponent.isAuditPathEnabled(app, key); - AuditApplication auditApplication = new AuditApplication(name, key, enabled); - ret.put(name, auditApplication); - } - return ret; - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public boolean isAuditEnabled(String applicationName, String path) - { - return auditComponent.isAuditPathEnabled(applicationName, path); - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public void enableAudit(String applicationName, String path) - { - auditComponent.enableAudit(applicationName, path); - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public void disableAudit(String applicationName, String path) - { - auditComponent.disableAudit(applicationName, path); - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public int clearAudit(String applicationName) - { - Long now = Long.valueOf(System.currentTimeMillis()); - return auditComponent.deleteAuditEntries(applicationName, null, now); - } - - /** - * {@inheritDoc} - * @since 3.4 - */ - @Override - public int clearAudit(String applicationName, Long fromTime, Long toTime) - { - toTime = (toTime == null) ? Long.valueOf(System.currentTimeMillis()) : toTime; - return auditComponent.deleteAuditEntries(applicationName, fromTime, toTime); - } - - /** - * {@inheritDoc} - * @since 5.2.2 - */ - @Override - public int clearAuditByIdRange(String applicationName, Long fromId, Long toId) - { - return auditComponent.deleteAuditEntriesByIdRange(applicationName, fromId, toId); - } - - /** - * {@inheritDoc} - * @since 3.4 - */ - @Override - public int clearAudit(List auditEntryIds) - { - return auditComponent.deleteAuditEntries(auditEntryIds); - } - - /** - * {@inheritDoc} - * @since 3.3 - */ - public void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults) - { - auditComponent.auditQuery(callback, parameters, maxResults); - } - - /** - * {@inheritDoc} - */ - public HashMap getAuditMinMaxByApp(String applicationName, List extremes) - { - return auditComponent.getAuditMinMaxByApp(applicationName, extremes); - } - - /** - * {@inheritDoc} - */ - @Override - public int getAuditEntriesCountByApp(String applicationName) - { - return auditComponent.getAuditEntriesCountByApp(applicationName); - } - - /** - * {@inheritDoc} - */ - @Override public int getAuditEntriesCountByAppAndProperties(AuditQueryParameters parameters) - { - return auditComponent.getAuditEntriesCountByAppAndProperties(parameters); - } +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.audit; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.alfresco.service.cmr.audit.AuditQueryParameters; +import org.alfresco.service.cmr.audit.AuditService; + +/** + * The implementation of the AuditService for application auditing. + * + * @author Derek Hulley + * @since 3.2 + */ +public class AuditServiceImpl implements AuditService +{ + private AuditComponent auditComponent; + + public AuditServiceImpl() + { + super(); + } + + public void setAuditComponent(AuditComponent auditComponent) + { + this.auditComponent = auditComponent; + } + + /** + * {@inheritDoc} + * @since 3.4 + */ + public boolean isAuditEnabled() + { + return auditComponent.isAuditEnabled(); + } + + /** + * {@inheritDoc} + * @since 3.4 + */ + @Override + public void setAuditEnabled(boolean enable) + { + auditComponent.setAuditEnabled(enable); + } + + /** + * {@inheritDoc} + * @since 3.4 + */ + @Override + public Map getAuditApplications() + { + Map apps = auditComponent.getAuditApplications(); + + Map ret = new TreeMap(); + for (String app : apps.keySet()) + { + String name = app; + String key = org.alfresco.repo.audit.model.AuditApplication.AUDIT_PATH_SEPARATOR + apps.get(app).getApplicationKey(); + boolean enabled = auditComponent.isAuditPathEnabled(app, key); + AuditApplication auditApplication = new AuditApplication(name, key, enabled); + ret.put(name, auditApplication); + } + return ret; + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public boolean isAuditEnabled(String applicationName, String path) + { + return auditComponent.isAuditPathEnabled(applicationName, path); + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public void enableAudit(String applicationName, String path) + { + auditComponent.enableAudit(applicationName, path); + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public void disableAudit(String applicationName, String path) + { + auditComponent.disableAudit(applicationName, path); + } + + /** + * {@inheritDoc} + * @since 3.2 + */ + public int clearAudit(String applicationName) + { + Long now = Long.valueOf(System.currentTimeMillis()); + return auditComponent.deleteAuditEntries(applicationName, null, now); + } + + /** + * {@inheritDoc} + * @since 3.4 + */ + @Override + public int clearAudit(String applicationName, Long fromTime, Long toTime) + { + toTime = (toTime == null) ? Long.valueOf(System.currentTimeMillis()) : toTime; + return auditComponent.deleteAuditEntries(applicationName, fromTime, toTime); + } + + /** + * {@inheritDoc} + * @since 5.2.2 + */ + @Override + public int clearAuditByIdRange(String applicationName, Long fromId, Long toId) + { + return auditComponent.deleteAuditEntriesByIdRange(applicationName, fromId, toId); + } + + /** + * {@inheritDoc} + * @since 3.4 + */ + @Override + public int clearAudit(List auditEntryIds) + { + return auditComponent.deleteAuditEntries(auditEntryIds); + } + + /** + * {@inheritDoc} + * @since 3.3 + */ + public void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults) + { + auditComponent.auditQuery(callback, parameters, maxResults); + } + + /** + * {@inheritDoc} + */ + public HashMap getAuditMinMaxByApp(String applicationName, List extremes) + { + return auditComponent.getAuditMinMaxByApp(applicationName, extremes); + } + + /** + * {@inheritDoc} + */ + @Override + public int getAuditEntriesCountByApp(String applicationName) + { + return auditComponent.getAuditEntriesCountByApp(applicationName); + } + + /** + * {@inheritDoc} + */ + @Override public int getAuditEntriesCountByAppAndProperties(String applicationName, AuditQueryParameters parameters) + { + return auditComponent.getAuditEntriesCountByAppAndProperties(applicationName, parameters); + } } \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/domain/CrcHelper.java b/repository/src/main/java/org/alfresco/repo/domain/CrcHelper.java index d3cdb157b6..07be20c6a7 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/CrcHelper.java +++ b/repository/src/main/java/org/alfresco/repo/domain/CrcHelper.java @@ -1,123 +1,122 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2024 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ - -package org.alfresco.repo.domain; - -import java.io.UnsupportedEncodingException; -import java.util.zip.CRC32; - -import org.alfresco.util.Pair; - -/** - * Helper class to calculate CRC values for string persistence. - * - * @author Derek Hulley - * @since 3.2 - */ -public class CrcHelper -{ - public static final String EMPTY_STRING = ".empty"; - - /** - * Calculate a persistable, unique pair of values that can be persisted in a database unique - * key and guarantee correct case-sensitivity. - *

- * While the short-string version of the value is always lowercase, the CRC is - * calculated from the virgin string if case-sensitivity is enforced; in the case-insensitive - * case, the CRC is calculated from a lowercase version of the string. - *

- * If the value is an empty string, then {@link #EMPTY_STRING} is used instead. This ensures - * that persisted values don't fall foul of the Oracle empty string comparison "behaviour" i.e - * you should never persist an empty string in Oracle as it equates to a SQL NULL. - * - * @param value the raw value that will be persisted - * @param dataLength the maximum number of characters that can be persisted - * @param useCharsFromStart true if the shortened string value must be made from - * the first characters of the string or false to use - * characters from the end of the string. - * @param caseSensitive true if the resulting pair must be case-sensitive or - * false if the pair must be case-insensitive. - * @return Return the persistable pair. The result will never be null, - * but the individual pair values will be null if the - * value given is null - */ - public static Pair getStringCrcPair( - String value, - int dataLength, - boolean useCharsFromStart, - boolean caseSensitive) - { - String valueLowerCase; - if (value == null) - { - return new Pair(null, null); - } - else if (value.length() == 0) - { - value = CrcHelper.EMPTY_STRING; - valueLowerCase = value; - } - else - { - valueLowerCase = value.toLowerCase(); - } - Long valueCrc; - try - { - CRC32 crc = new CRC32(); - if (caseSensitive) - { - crc.update(value.getBytes("UTF-8")); - } - else - { - crc.update(valueLowerCase.getBytes("UTF-8")); - } - valueCrc = crc.getValue(); - } - catch (UnsupportedEncodingException e) - { - throw new RuntimeException("UTF-8 encoding is not supported"); - } - // Crc Value will change based on the case-sensitive, So we need to get the short value based on case-sensitive - String valueShort = null; - String currentValue = caseSensitive ? value : valueLowerCase; - int valueLen = currentValue.length(); - if (valueLen < dataLength) - { - valueShort = currentValue; - } - else if (useCharsFromStart) - { - valueShort = currentValue.substring(0, dataLength - 1); - } - else - { - valueShort = currentValue.substring(valueLen - dataLength); - } - return new Pair(valueShort, valueCrc); - } +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.repo.domain; + +import java.io.UnsupportedEncodingException; +import java.util.zip.CRC32; + +import org.alfresco.util.Pair; + +/** + * Helper class to calculate CRC values for string persistence. + * + * @author Derek Hulley + * @since 3.2 + */ +public class CrcHelper +{ + public static final String EMPTY_STRING = ".empty"; + + /** + * Calculate a persistable, unique pair of values that can be persisted in a database unique + * key and guarantee correct case-sensitivity. + *

+ * While the short-string version of the value is always lowercase, the CRC is + * calculated from the virgin string if case-sensitivity is enforced; in the case-insensitive + * case, the CRC is calculated from a lowercase version of the string. + *

+ * If the value is an empty string, then {@link #EMPTY_STRING} is used instead. This ensures + * that persisted values don't fall foul of the Oracle empty string comparison "behaviour" i.e + * you should never persist an empty string in Oracle as it equates to a SQL NULL. + * + * @param value the raw value that will be persisted + * @param dataLength the maximum number of characters that can be persisted + * @param useCharsFromStart true if the shortened string value must be made from + * the first characters of the string or false to use + * characters from the end of the string. + * @param caseSensitive true if the resulting pair must be case-sensitive or + * false if the pair must be case-insensitive. + * @return Return the persistable pair. The result will never be null, + * but the individual pair values will be null if the + * value given is null + */ + public static Pair getStringCrcPair( + String value, + int dataLength, + boolean useCharsFromStart, + boolean caseSensitive) + { + String valueLowerCase; + if (value == null) + { + return new Pair(null, null); + } + else if (value.length() == 0) + { + value = CrcHelper.EMPTY_STRING; + valueLowerCase = value; + } + else + { + valueLowerCase = value.toLowerCase(); + } + Long valueCrc; + try + { + CRC32 crc = new CRC32(); + if (caseSensitive) + { + crc.update(value.getBytes("UTF-8")); + } + else + { + crc.update(valueLowerCase.getBytes("UTF-8")); + } + valueCrc = crc.getValue(); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException("UTF-8 encoding is not supported"); + } + // Get the short value (case-sensitive or not) + String valueShort = null; + int valueLen = valueLowerCase.length(); + if (valueLen < dataLength) + { + valueShort = valueLowerCase; + } + else if (useCharsFromStart) + { + valueShort = valueLowerCase.substring(0, dataLength - 1); + } + else + { + valueShort = valueLowerCase.substring(valueLen - dataLength); + } + return new Pair(valueShort, valueCrc); + } } \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/domain/audit/AuditDAO.java b/repository/src/main/java/org/alfresco/repo/domain/audit/AuditDAO.java index cac517706e..45739bbf9a 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/audit/AuditDAO.java +++ b/repository/src/main/java/org/alfresco/repo/domain/audit/AuditDAO.java @@ -1,258 +1,259 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2024 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.repo.domain.audit; - -import java.io.Serializable; -import java.net.URL; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback; -import org.alfresco.service.cmr.repository.ContentData; -import org.alfresco.util.Pair; - -/** - * DAO services for alf_audit_XXX tables. - *

- * The older methods are supported by a different implementation and will eventually - * be deprecated and phased out. - * - * @author Derek Hulley - * @since 3.2 - */ -public interface AuditDAO -{ - /* - * V3.2 methods after here only, please - */ - - /** - * Information about the audit application to be passed in an out of the interface. - * - * @author Derek Hulley - * @since 3.2 - */ - public static class AuditApplicationInfo - { - private Long id; - private String name; - private Long modelId; - private Long disabledPathsId; - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append("AuditApplicationInfo ") - .append("[ id=").append(id) - .append(", name=").append(name) - .append(", modelId=").append(modelId) - .append(", disabledPathsId=").append(disabledPathsId) - .append("]"); - return sb.toString(); - } - - public Long getId() - { - return id; - } - public void setId(Long id) - { - this.id = id; - } - public String getName() - { - return name; - } - public void setname(String name) - { - this.name = name; - } - public Long getModelId() - { - return modelId; - } - public void setModelId(Long modelId) - { - this.modelId = modelId; - } - public Long getDisabledPathsId() - { - return disabledPathsId; - } - public void setDisabledPathsId(Long disabledPathsId) - { - this.disabledPathsId = disabledPathsId; - } - } - - /** - * Creates a new audit model entry or finds an existing one - * - * @param url the URL of the configuration - * @return Returns the ID of the config matching the input stream and the - * content storage details - * @since 3.2 - */ - Pair getOrCreateAuditModel(URL url); - - /** - * Get the audit application details. - * - * @param applicationName the name of the application - * @return Returns details of an existing application or null if it doesn't exist - * - * @since 3.2 - */ - AuditApplicationInfo getAuditApplication(String applicationName); - - /** - * Creates a new audit application. The application name must be unique. - * - * @param application the name of the application - * @param modelId the ID of the model configuration - * - * @since 3.2 - */ - AuditApplicationInfo createAuditApplication(String application, Long modelId); - - /** - * Update the audit application to refer to a new model. - * If the model did not change, then nothing will be done. - * - * @param id the ID of the audit application - * @param modelId the ID of the new model - * - * @since 3.2 - */ - void updateAuditApplicationModel(Long id, Long modelId); - - /** - * Update the audit application to hold a new set of disabled paths. - * If the value did not change, then nothing will be done. - * - * @param id the ID of the audit application - * @param disabledPaths the new disabled paths - * - * @since 3.2 - */ - void updateAuditApplicationDisabledPaths(Long id, Set disabledPaths); - - /** - * Delete audit entries for the application, possibly limiting the time range. - * - * @param applicationId an existing audit application ID - * @param fromTime the minimum entry time (inclusive, optional) - * @param toTime the maximum entry time (exclusive, optional) - * @return Returns the number of entries deleted - * - * @since 3.2 - */ - int deleteAuditEntries(Long applicationId, Long fromTime, Long toTime); - - /** - * Delete audit entries for the application for given id range. - * - * @param applicationId an existing audit application ID - * @param fromId the minimum fromId (inclusive, optional) - * @param toId the maximum toId (exclusive, optional) - * @return Returns the number of entries deleted - * - * @since 5.2.2 - */ - int deleteAuditEntriesByIdRange(Long applicationId, Long fromId, Long toId); - - /** - * Delete a discrete list of audit entries. Duplicate entries are collapsed - * and the number of entries deleted will match the count of unique IDs in - * the list; otherwise a concurrency condition has occured and an exception - * will be generated. - * - * @param auditEntryIds the IDs of all audit entries to delete - * @return Returns the number of entries deleted - */ - int deleteAuditEntries(List auditEntryIds); - - /** - * Create a new audit entry with the given map of values. - * - * @param applicationId an existing audit application ID - * @param time the time (ms since epoch) to log the entry against - * @param username the authenticated user (null if not present) - * @param values the values to record - * @return Returns the unique entry ID - * - * @since 3.2 - */ - Long createAuditEntry(Long applicationId, long time, String username, Map values); - - /** - * Find audit entries using the given parameters, any of which may be null - * - * @param callback the data callback per entry - * @param parameters the parameters for the query (may not be null) - * @param maxResults the maximum number of results to retrieve (must be greater than 0) - * - * @throws IllegalArgumentException if maxResults less or equal to zero - */ - void findAuditEntries( - AuditQueryCallback callback, - org.alfresco.service.cmr.audit.AuditQueryParameters parameters, - int maxResults); - - /** - * Issue an audit query to retrieve min / max audit record id for a given application. - * - * @param appId the database id of the application - * @param extremes a list containing min/max or both - * @return a map containing min/max and the associated value - */ - HashMap getAuditMinMaxByApp(long appId, List extremes); - - /** - * Issue an audit query to retrieve count of records for a given application. - * - * @param applicationId the database id of the application - * @return a map containing min/max and the associated value - */ - default int getAuditEntriesCountByApp(long applicationId) - { - return -1; - } - - /** - * Issue an audit query to retrieve count of records for a given application and properties - * - * @param parameters audit parameters provided by the where clause on the ReST API - * @return a map containing min/max and the associated value - */ - default int getAuditEntriesCountByAppAndProperties(org.alfresco.service.cmr.audit.AuditQueryParameters parameters) - { - return -1; - } +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.domain.audit; + +import java.io.Serializable; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.util.Pair; + +/** + * DAO services for alf_audit_XXX tables. + *

+ * The older methods are supported by a different implementation and will eventually + * be deprecated and phased out. + * + * @author Derek Hulley + * @since 3.2 + */ +public interface AuditDAO +{ + /* + * V3.2 methods after here only, please + */ + + /** + * Information about the audit application to be passed in an out of the interface. + * + * @author Derek Hulley + * @since 3.2 + */ + public static class AuditApplicationInfo + { + private Long id; + private String name; + private Long modelId; + private Long disabledPathsId; + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("AuditApplicationInfo ") + .append("[ id=").append(id) + .append(", name=").append(name) + .append(", modelId=").append(modelId) + .append(", disabledPathsId=").append(disabledPathsId) + .append("]"); + return sb.toString(); + } + + public Long getId() + { + return id; + } + public void setId(Long id) + { + this.id = id; + } + public String getName() + { + return name; + } + public void setname(String name) + { + this.name = name; + } + public Long getModelId() + { + return modelId; + } + public void setModelId(Long modelId) + { + this.modelId = modelId; + } + public Long getDisabledPathsId() + { + return disabledPathsId; + } + public void setDisabledPathsId(Long disabledPathsId) + { + this.disabledPathsId = disabledPathsId; + } + } + + /** + * Creates a new audit model entry or finds an existing one + * + * @param url the URL of the configuration + * @return Returns the ID of the config matching the input stream and the + * content storage details + * @since 3.2 + */ + Pair getOrCreateAuditModel(URL url); + + /** + * Get the audit application details. + * + * @param applicationName the name of the application + * @return Returns details of an existing application or null if it doesn't exist + * + * @since 3.2 + */ + AuditApplicationInfo getAuditApplication(String applicationName); + + /** + * Creates a new audit application. The application name must be unique. + * + * @param application the name of the application + * @param modelId the ID of the model configuration + * + * @since 3.2 + */ + AuditApplicationInfo createAuditApplication(String application, Long modelId); + + /** + * Update the audit application to refer to a new model. + * If the model did not change, then nothing will be done. + * + * @param id the ID of the audit application + * @param modelId the ID of the new model + * + * @since 3.2 + */ + void updateAuditApplicationModel(Long id, Long modelId); + + /** + * Update the audit application to hold a new set of disabled paths. + * If the value did not change, then nothing will be done. + * + * @param id the ID of the audit application + * @param disabledPaths the new disabled paths + * + * @since 3.2 + */ + void updateAuditApplicationDisabledPaths(Long id, Set disabledPaths); + + /** + * Delete audit entries for the application, possibly limiting the time range. + * + * @param applicationId an existing audit application ID + * @param fromTime the minimum entry time (inclusive, optional) + * @param toTime the maximum entry time (exclusive, optional) + * @return Returns the number of entries deleted + * + * @since 3.2 + */ + int deleteAuditEntries(Long applicationId, Long fromTime, Long toTime); + + /** + * Delete audit entries for the application for given id range. + * + * @param applicationId an existing audit application ID + * @param fromId the minimum fromId (inclusive, optional) + * @param toId the maximum toId (exclusive, optional) + * @return Returns the number of entries deleted + * + * @since 5.2.2 + */ + int deleteAuditEntriesByIdRange(Long applicationId, Long fromId, Long toId); + + /** + * Delete a discrete list of audit entries. Duplicate entries are collapsed + * and the number of entries deleted will match the count of unique IDs in + * the list; otherwise a concurrency condition has occured and an exception + * will be generated. + * + * @param auditEntryIds the IDs of all audit entries to delete + * @return Returns the number of entries deleted + */ + int deleteAuditEntries(List auditEntryIds); + + /** + * Create a new audit entry with the given map of values. + * + * @param applicationId an existing audit application ID + * @param time the time (ms since epoch) to log the entry against + * @param username the authenticated user (null if not present) + * @param values the values to record + * @return Returns the unique entry ID + * + * @since 3.2 + */ + Long createAuditEntry(Long applicationId, long time, String username, Map values); + + /** + * Find audit entries using the given parameters, any of which may be null + * + * @param callback the data callback per entry + * @param parameters the parameters for the query (may not be null) + * @param maxResults the maximum number of results to retrieve (must be greater than 0) + * + * @throws IllegalArgumentException if maxResults less or equal to zero + */ + void findAuditEntries( + AuditQueryCallback callback, + org.alfresco.service.cmr.audit.AuditQueryParameters parameters, + int maxResults); + + /** + * Issue an audit query to retrieve min / max audit record id for a given application. + * + * @param appId the database id of the application + * @param extremes a list containing min/max or both + * @return a map containing min/max and the associated value + */ + HashMap getAuditMinMaxByApp(long appId, List extremes); + + /** + * Issue an audit query to retrieve count of records for a given application. + * + * @param applicationId the database id of the application + * @return a map containing min/max and the associated value + */ + default int getAuditEntriesCountByApp(long applicationId) + { + return -1; + } + + /** + * Issue an audit query to retrieve count of records for a given application and properties + * + * @param applicationName name of the application to be queried + * @param parameters audit parameters provided by the where clause on the ReST API + * @return a map containing min/max and the associated value + */ + default int getAuditEntriesCountByAppAndProperties(String applicationName, org.alfresco.service.cmr.audit.AuditQueryParameters parameters) + { + return -1; + } } \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java b/repository/src/main/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java index 12468dbf8e..c1d3bdc700 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java +++ b/repository/src/main/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java @@ -1,316 +1,315 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2024 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.repo.domain.audit.ibatis; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.alfresco.repo.domain.audit.AbstractAuditDAOImpl; -import org.alfresco.repo.domain.audit.AuditApplicationEntity; -import org.alfresco.repo.domain.audit.AuditDeleteParameters; -import org.alfresco.repo.domain.audit.AuditEntryEntity; -import org.alfresco.repo.domain.audit.AuditModelEntity; -import org.alfresco.repo.domain.audit.AuditQueryParameters; -import org.alfresco.repo.domain.audit.AuditQueryResult; -import org.alfresco.repo.domain.propval.PropertyValueDAO.PropertyFinderCallback; -import org.alfresco.util.Pair; -import org.apache.ibatis.session.RowBounds; -import org.mybatis.spring.SqlSessionTemplate; -import org.springframework.dao.ConcurrencyFailureException; - -/** - * iBatis-specific implementation of the DAO for alf_audit_XXX tables. - * - * @author Derek Hulley - * @since 3.2 - */ -public class AuditDAOImpl extends AbstractAuditDAOImpl -{ - private static final String SELECT_MODEL_BY_CRC = "alfresco.audit.select_AuditModelByCrc"; - private static final String INSERT_MODEL = "alfresco.audit.insert.insert_AuditModel"; - - private static final String SELECT_APPLICATION_BY_ID = "alfresco.audit.select_AuditApplicationById"; - private static final String SELECT_APPLICATION_BY_NAME_ID = "alfresco.audit.select_AuditApplicationByNameId"; - private static final String INSERT_APPLICATION = "alfresco.audit.insert.insert_AuditApplication"; - private static final String UPDATE_APPLICATION = "alfresco.audit.update_AuditApplication"; - - private static final String DELETE_ENTRIES = "alfresco.audit.delete_AuditEntries"; - private static final String DELETE_ENTRIES_BY_ID = "alfresco.audit.delete_AuditEntriesById"; - private static final String INSERT_ENTRY = "alfresco.audit.insert.insert_AuditEntry"; - private static final String SELECT_MINMAX_ENTRY_FOR_APP = "alfresco.audit.select_MinMaxAuditEntryId"; - private static final String SELECT_COUNT_ENTRIES_FOR_APP = "alfresco.audit.select_CountAuditEntryId"; - private static final String SELECT_COUNT_ENTRIES_FOR_APP_WITH_PROPERTIES = "select_CountAuditEntryIdWithWhereClause"; - - @SuppressWarnings("unused") - private static final String SELECT_ENTRIES_SIMPLE = "alfresco.audit.select_AuditEntriesSimple"; - private static final String SELECT_ENTRIES_WITH_VALUES = "alfresco.audit.select_AuditEntriesWithValues"; - private static final String SELECT_ENTRIES_WITHOUT_VALUES = "alfresco.audit.select_AuditEntriesWithoutValues"; - - private SqlSessionTemplate template; - - public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) - { - this.template = sqlSessionTemplate; - } - - @Override - protected AuditModelEntity getAuditModelByCrc(long crc) - { - AuditModelEntity entity = new AuditModelEntity(); - entity.setContentCrc(crc); - entity = template.selectOne( - SELECT_MODEL_BY_CRC, - entity); - // Done - return entity; - } - - @Override - protected AuditModelEntity createAuditModel(Long contentDataId, long crc) - { - AuditModelEntity entity = new AuditModelEntity(); - entity.setContentDataId(contentDataId); - entity.setContentCrc(crc); - template.insert(INSERT_MODEL, entity); - return entity; - } - - @Override - protected AuditApplicationEntity getAuditApplicationById(Long id) - { - Map params = new HashMap(11); - params.put("id", id); - AuditApplicationEntity entity = template.selectOne( - SELECT_APPLICATION_BY_ID, - params); - // Done - if (logger.isDebugEnabled()) - { - logger.debug("Searched for audit application ID " + id + " and found: " + entity); - } - return entity; - } - - @Override - protected AuditApplicationEntity getAuditApplicationByName(String appName) - { - // Resolve the name as a property ID - Pair appNamePair = propertyValueDAO.getPropertyValue(appName); - if (appNamePair == null) - { - // There will be no results - return null; - } - - Map params = new HashMap(11); - params.put("id", appNamePair.getFirst()); - AuditApplicationEntity entity = template.selectOne( - SELECT_APPLICATION_BY_NAME_ID, - params); - // Done - if (logger.isDebugEnabled()) - { - logger.debug("Searched for audit application '" + appName + "' and found: " + entity); - } - return entity; - } - - @Override - protected AuditApplicationEntity createAuditApplication(Long appNameId, Long modelId, Long disabledPathsId) - { - AuditApplicationEntity entity = new AuditApplicationEntity(); - entity.setVersion((short)0); - entity.setApplicationNameId(appNameId); - entity.setAuditModelId(modelId); - entity.setDisabledPathsId(disabledPathsId); - template.insert(INSERT_APPLICATION, entity); - return entity; - } - - @Override - protected AuditApplicationEntity updateAuditApplication(AuditApplicationEntity entity) - { - AuditApplicationEntity updateEntity = new AuditApplicationEntity(); - updateEntity.setId(entity.getId()); - updateEntity.setVersion(entity.getVersion()); - updateEntity.incrementVersion(); - updateEntity.setApplicationNameId(entity.getApplicationNameId()); - updateEntity.setAuditModelId(entity.getAuditModelId()); - updateEntity.setDisabledPathsId(entity.getDisabledPathsId()); - - int updated = template.update(UPDATE_APPLICATION, updateEntity); - if (updated != 1) - { - // unexpected number of rows affected - throw new ConcurrencyFailureException("Incorrect number of rows affected for updateAuditApplication: " + updateEntity + ": expected 1, actual " + updated); - } - - // Done - return updateEntity; - } - - public int deleteAuditEntries(Long applicationId, Long from, Long to) - { - AuditDeleteParameters params = new AuditDeleteParameters(); - params.setAuditApplicationId(applicationId); - params.setAuditFromTime(from); - params.setAuditToTime(to); - return template.delete(DELETE_ENTRIES, params); - } - - public int deleteAuditEntriesByIdRange(Long applicationId, Long fromId, Long toId) - { - AuditDeleteParameters params = new AuditDeleteParameters(); - params.setAuditApplicationId(applicationId); - params.setAuditFromId(fromId); - params.setAuditToId(toId); - return template.delete(DELETE_ENTRIES, params); - } - - @Override - protected int deleteAuditEntriesImpl(List auditEntryIds) - { - AuditDeleteParameters params = new AuditDeleteParameters(); - params.setAuditEntryIds(auditEntryIds); - return template.delete(DELETE_ENTRIES_BY_ID, params); - } - - @Override - protected AuditEntryEntity createAuditEntry(Long applicationId, long time, Long usernameId, Long valuesId) - { - AuditEntryEntity entity = new AuditEntryEntity(); - entity.setAuditApplicationId(applicationId); - entity.setAuditTime(time); - entity.setAuditUserId(usernameId); - entity.setAuditValuesId(valuesId); - template.insert(INSERT_ENTRY, entity); - return entity; - } - - public HashMap getAuditMinMaxByApp(long appId, List extremes) - { - // Build parameters to be used in the query. Filter the duplicates when inserting into map - Map params = extremes.stream().collect(Collectors.toMap(s -> s, s -> Boolean.TRUE, (s1, s2) -> s1)); - params.put("auditAppId", appId); - - HashMap result = template.selectOne(SELECT_MINMAX_ENTRY_FOR_APP, params); - - return result; - } - - @Override - public int getAuditEntriesCountByApp(long applicationId) - { - Map params = new HashMap<>(); - params.put("auditAppId", applicationId); - - int result = template.selectOne(SELECT_COUNT_ENTRIES_FOR_APP, params); - - return result; - } - - @Override - public int getAuditEntriesCountByAppAndProperties(org.alfresco.service.cmr.audit.AuditQueryParameters parameters) - { - AuditQueryParameters dbParameters = convertFromRestAuditQueryParameters(parameters); - - int result = template.selectOne(SELECT_COUNT_ENTRIES_FOR_APP_WITH_PROPERTIES, dbParameters); - - return result; - } - - @SuppressWarnings("unchecked") - @Override - protected void findAuditEntries( - final AuditQueryRowHandler rowHandler, - int maxResults, - org.alfresco.service.cmr.audit.AuditQueryParameters restParameters) - { - AuditQueryParameters params = convertFromRestAuditQueryParameters(restParameters); - if (params==null) - { - return; - } - - if (maxResults > 0) - { - // Query without getting the values. We gather all the results and batch-fetch the audited - // values afterwards. - final Map resultsByValueId = new HashMap(173); - PropertyFinderCallback propertyFinderCallback = new PropertyFinderCallback() - { - public void handleProperty(Long id, Serializable value) - { - // get the row - AuditQueryResult row = resultsByValueId.get(id); - try - { - row.setAuditValue((Map) value); - } - catch (ClassCastException e) - { - // The handler will deal with the entry - } - } - }; - - List rows = template.selectList(SELECT_ENTRIES_WITHOUT_VALUES, params, new RowBounds(0, maxResults)); - for (AuditQueryResult row : rows) - { - resultsByValueId.put(row.getAuditValuesId(), row); - if (resultsByValueId.size() >= 100) - { - // Fetch values for the results. The treemap is ordered. - List valueIds = new ArrayList(resultsByValueId.keySet()); - propertyValueDAO.getPropertiesByIds(valueIds, propertyFinderCallback); - // Clear and continue - resultsByValueId.clear(); - } - } - // Process any remaining results - if (resultsByValueId.size() > 0) - { - // Fetch values for the results. The treemap is ordered. - List valueIds = new ArrayList(resultsByValueId.keySet()); - propertyValueDAO.getPropertiesByIds(valueIds, propertyFinderCallback); - } - // Now pass the filled-out results to the row handler (order-preserved) - for (AuditQueryResult row : rows) - { - rowHandler.processResult(row); - } - } - else - { - throw new IllegalArgumentException("maxResults must be greater than 0"); - } - } -} - +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.domain.audit.ibatis; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.alfresco.repo.domain.audit.AbstractAuditDAOImpl; +import org.alfresco.repo.domain.audit.AuditApplicationEntity; +import org.alfresco.repo.domain.audit.AuditDeleteParameters; +import org.alfresco.repo.domain.audit.AuditEntryEntity; +import org.alfresco.repo.domain.audit.AuditModelEntity; +import org.alfresco.repo.domain.audit.AuditQueryParameters; +import org.alfresco.repo.domain.audit.AuditQueryResult; +import org.alfresco.repo.domain.propval.PropertyValueDAO.PropertyFinderCallback; +import org.alfresco.util.Pair; +import org.apache.ibatis.session.RowBounds; +import org.mybatis.spring.SqlSessionTemplate; +import org.springframework.dao.ConcurrencyFailureException; + +/** + * iBatis-specific implementation of the DAO for alf_audit_XXX tables. + * + * @author Derek Hulley + * @since 3.2 + */ +public class AuditDAOImpl extends AbstractAuditDAOImpl +{ + private static final String SELECT_MODEL_BY_CRC = "alfresco.audit.select_AuditModelByCrc"; + private static final String INSERT_MODEL = "alfresco.audit.insert.insert_AuditModel"; + + private static final String SELECT_APPLICATION_BY_ID = "alfresco.audit.select_AuditApplicationById"; + private static final String SELECT_APPLICATION_BY_NAME_ID = "alfresco.audit.select_AuditApplicationByNameId"; + private static final String INSERT_APPLICATION = "alfresco.audit.insert.insert_AuditApplication"; + private static final String UPDATE_APPLICATION = "alfresco.audit.update_AuditApplication"; + + private static final String DELETE_ENTRIES = "alfresco.audit.delete_AuditEntries"; + private static final String DELETE_ENTRIES_BY_ID = "alfresco.audit.delete_AuditEntriesById"; + private static final String INSERT_ENTRY = "alfresco.audit.insert.insert_AuditEntry"; + private static final String SELECT_MINMAX_ENTRY_FOR_APP = "alfresco.audit.select_MinMaxAuditEntryId"; + private static final String SELECT_COUNT_ENTRIES_FOR_APP = "alfresco.audit.select_CountAuditEntryId"; + private static final String SELECT_COUNT_ENTRIES_FOR_APP_WITH_PROPERTIES = "select_CountAuditEntryIdWithWhereClause"; + + @SuppressWarnings("unused") + private static final String SELECT_ENTRIES_SIMPLE = "alfresco.audit.select_AuditEntriesSimple"; + private static final String SELECT_ENTRIES_WITH_VALUES = "alfresco.audit.select_AuditEntriesWithValues"; + private static final String SELECT_ENTRIES_WITHOUT_VALUES = "alfresco.audit.select_AuditEntriesWithoutValues"; + + private SqlSessionTemplate template; + + public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) + { + this.template = sqlSessionTemplate; + } + + @Override + protected AuditModelEntity getAuditModelByCrc(long crc) + { + AuditModelEntity entity = new AuditModelEntity(); + entity.setContentCrc(crc); + entity = template.selectOne( + SELECT_MODEL_BY_CRC, + entity); + // Done + return entity; + } + + @Override + protected AuditModelEntity createAuditModel(Long contentDataId, long crc) + { + AuditModelEntity entity = new AuditModelEntity(); + entity.setContentDataId(contentDataId); + entity.setContentCrc(crc); + template.insert(INSERT_MODEL, entity); + return entity; + } + + @Override + protected AuditApplicationEntity getAuditApplicationById(Long id) + { + Map params = new HashMap(11); + params.put("id", id); + AuditApplicationEntity entity = template.selectOne( + SELECT_APPLICATION_BY_ID, + params); + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Searched for audit application ID " + id + " and found: " + entity); + } + return entity; + } + + @Override + protected AuditApplicationEntity getAuditApplicationByName(String appName) + { + // Resolve the name as a property ID + Pair appNamePair = propertyValueDAO.getPropertyValue(appName); + if (appNamePair == null) + { + // There will be no results + return null; + } + + Map params = new HashMap(11); + params.put("id", appNamePair.getFirst()); + AuditApplicationEntity entity = template.selectOne( + SELECT_APPLICATION_BY_NAME_ID, + params); + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Searched for audit application '" + appName + "' and found: " + entity); + } + return entity; + } + + @Override + protected AuditApplicationEntity createAuditApplication(Long appNameId, Long modelId, Long disabledPathsId) + { + AuditApplicationEntity entity = new AuditApplicationEntity(); + entity.setVersion((short)0); + entity.setApplicationNameId(appNameId); + entity.setAuditModelId(modelId); + entity.setDisabledPathsId(disabledPathsId); + template.insert(INSERT_APPLICATION, entity); + return entity; + } + + @Override + protected AuditApplicationEntity updateAuditApplication(AuditApplicationEntity entity) + { + AuditApplicationEntity updateEntity = new AuditApplicationEntity(); + updateEntity.setId(entity.getId()); + updateEntity.setVersion(entity.getVersion()); + updateEntity.incrementVersion(); + updateEntity.setApplicationNameId(entity.getApplicationNameId()); + updateEntity.setAuditModelId(entity.getAuditModelId()); + updateEntity.setDisabledPathsId(entity.getDisabledPathsId()); + + int updated = template.update(UPDATE_APPLICATION, updateEntity); + if (updated != 1) + { + // unexpected number of rows affected + throw new ConcurrencyFailureException("Incorrect number of rows affected for updateAuditApplication: " + updateEntity + ": expected 1, actual " + updated); + } + + // Done + return updateEntity; + } + + public int deleteAuditEntries(Long applicationId, Long from, Long to) + { + AuditDeleteParameters params = new AuditDeleteParameters(); + params.setAuditApplicationId(applicationId); + params.setAuditFromTime(from); + params.setAuditToTime(to); + return template.delete(DELETE_ENTRIES, params); + } + + public int deleteAuditEntriesByIdRange(Long applicationId, Long fromId, Long toId) + { + AuditDeleteParameters params = new AuditDeleteParameters(); + params.setAuditApplicationId(applicationId); + params.setAuditFromId(fromId); + params.setAuditToId(toId); + return template.delete(DELETE_ENTRIES, params); + } + + @Override + protected int deleteAuditEntriesImpl(List auditEntryIds) + { + AuditDeleteParameters params = new AuditDeleteParameters(); + params.setAuditEntryIds(auditEntryIds); + return template.delete(DELETE_ENTRIES_BY_ID, params); + } + + @Override + protected AuditEntryEntity createAuditEntry(Long applicationId, long time, Long usernameId, Long valuesId) + { + AuditEntryEntity entity = new AuditEntryEntity(); + entity.setAuditApplicationId(applicationId); + entity.setAuditTime(time); + entity.setAuditUserId(usernameId); + entity.setAuditValuesId(valuesId); + template.insert(INSERT_ENTRY, entity); + return entity; + } + + public HashMap getAuditMinMaxByApp(long appId, List extremes) + { + // Build parameters to be used in the query. Filter the duplicates when inserting into map + Map params = extremes.stream().collect(Collectors.toMap(s -> s, s -> Boolean.TRUE, (s1, s2) -> s1)); + params.put("auditAppId", appId); + + HashMap result = template.selectOne(SELECT_MINMAX_ENTRY_FOR_APP, params); + + return result; + } + + @Override + public int getAuditEntriesCountByApp(long applicationId) + { + Map params = new HashMap<>(); + params.put("auditAppId", applicationId); + + int result = template.selectOne(SELECT_COUNT_ENTRIES_FOR_APP, params); + + return result; + } + + @Override + public int getAuditEntriesCountByAppAndProperties(String applicationName, org.alfresco.service.cmr.audit.AuditQueryParameters parameters) + { + AuditQueryParameters dbParameters = convertFromRestAuditQueryParameters(parameters); + + int result = template.selectOne(SELECT_COUNT_ENTRIES_FOR_APP_WITH_PROPERTIES, dbParameters); + + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected void findAuditEntries( + final AuditQueryRowHandler rowHandler, + int maxResults, + org.alfresco.service.cmr.audit.AuditQueryParameters restParameters) + { + AuditQueryParameters params = convertFromRestAuditQueryParameters(restParameters); + if (params==null) + { + return; + } + + if (maxResults > 0) + { + // Query without getting the values. We gather all the results and batch-fetch the audited + // values afterwards. + final Map resultsByValueId = new HashMap(173); + PropertyFinderCallback propertyFinderCallback = new PropertyFinderCallback() + { + public void handleProperty(Long id, Serializable value) + { + // get the row + AuditQueryResult row = resultsByValueId.get(id); + try + { + row.setAuditValue((Map) value); + } + catch (ClassCastException e) + { + // The handler will deal with the entry + } + } + }; + + List rows = template.selectList(SELECT_ENTRIES_WITHOUT_VALUES, params, new RowBounds(0, maxResults)); + for (AuditQueryResult row : rows) + { + resultsByValueId.put(row.getAuditValuesId(), row); + if (resultsByValueId.size() >= 100) + { + // Fetch values for the results. The treemap is ordered. + List valueIds = new ArrayList(resultsByValueId.keySet()); + propertyValueDAO.getPropertiesByIds(valueIds, propertyFinderCallback); + // Clear and continue + resultsByValueId.clear(); + } + } + // Process any remaining results + if (resultsByValueId.size() > 0) + { + // Fetch values for the results. The treemap is ordered. + List valueIds = new ArrayList(resultsByValueId.keySet()); + propertyValueDAO.getPropertiesByIds(valueIds, propertyFinderCallback); + } + // Now pass the filled-out results to the row handler (order-preserved) + for (AuditQueryResult row : rows) + { + rowHandler.processResult(row); + } + } + else + { + throw new IllegalArgumentException("maxResults must be greater than 0"); + } + } +} \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/domain/propval/PropertyStringValueEntity.java b/repository/src/main/java/org/alfresco/repo/domain/propval/PropertyStringValueEntity.java index 56e188fbf6..6b405d1b6e 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/propval/PropertyStringValueEntity.java +++ b/repository/src/main/java/org/alfresco/repo/domain/propval/PropertyStringValueEntity.java @@ -1,173 +1,159 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2024 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.repo.domain.propval; - -import org.alfresco.repo.domain.CrcHelper; -import org.alfresco.util.EqualsHelper; -import org.alfresco.util.Pair; - -/** - * Entity bean for alf_prop_string_value table. - * - * @author Derek Hulley - * @since 3.2 - */ -public class PropertyStringValueEntity -{ - public static final String EMPTY_STRING = ""; - public static final String EMPTY_STRING_REPLACEMENT = ".empty"; - - private Long id; - private String stringValue; - private String stringEndLower; - private Long stringCrc; - private String stringLower; - - public PropertyStringValueEntity() - { - } - - @Override - public int hashCode() - { - return (stringValue == null ? 0 : stringValue.hashCode()); - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && obj instanceof PropertyStringValueEntity) - { - PropertyStringValueEntity that = (PropertyStringValueEntity) obj; - return EqualsHelper.nullSafeEquals(this.stringValue, that.stringValue); - } - else - { - return false; - } - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(512); - sb.append("PropertyStringValueEntity") - .append("[ ID=").append(id) - .append(", stringValue=").append(stringValue) - .append("]"); - return sb.toString(); - } - - public Pair getEntityPair() - { - if (stringValue != null && stringValue.equals(PropertyStringValueEntity.EMPTY_STRING_REPLACEMENT)) - { - return new Pair(id, PropertyStringValueEntity.EMPTY_STRING); - } - else - { - return new Pair(id, stringValue); - } - } - - /** - * Set the string and string-end values - */ - public void setValue(String value) - { - if (value == null) - { - throw new IllegalArgumentException("Null strings cannot be persisted"); - } - if (value != null && value.equals(PropertyStringValueEntity.EMPTY_STRING)) - { - // Oracle: We can't insert empty strings into the column. - value = PropertyStringValueEntity.EMPTY_STRING_REPLACEMENT; - } - stringValue = value; - // Calculate the crc value from the original value - Pair crcPair = CrcHelper.getStringCrcPair(value, 16, false, true); - stringEndLower = crcPair.getFirst(); - stringCrc = crcPair.getSecond(); - // Calculate the crc value with case-insensitive - Pair crcPairWithCaseInSensitive = CrcHelper.getStringCrcPair(value, 16, false, false); - stringLower = crcPairWithCaseInSensitive.getFirst(); - } - - public Long getId() - { - return id; - } - - public void setId(Long id) - { - this.id = id; - } - - public String getStringValue() - { - return stringValue; - } - - public void setStringValue(String stringValue) - { - this.stringValue = stringValue; - } - - public String getStringEndLower() - { - return stringEndLower; - } - - public void setStringEndLower(String stringEndLower) - { - this.stringEndLower = stringEndLower; - } - - public Long getStringCrc() - { - return stringCrc; - } - - public void setStringCrc(Long stringCrc) - { - this.stringCrc = stringCrc; - } - - public String getStringLower() - { - return stringLower; - } - - public void setStringLower(String stringLower) - { - this.stringLower = stringLower; - } -} +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.domain.propval; + +import org.alfresco.repo.domain.CrcHelper; +import org.alfresco.util.EqualsHelper; +import org.alfresco.util.Pair; + +/** + * Entity bean for alf_prop_string_value table. + * + * @author Derek Hulley + * @since 3.2 + */ +public class PropertyStringValueEntity +{ + public static final String EMPTY_STRING = ""; + public static final String EMPTY_STRING_REPLACEMENT = ".empty"; + + private Long id; + private String stringValue; + private String stringEndLower; + private Long stringCrc; + + public PropertyStringValueEntity() + { + } + + @Override + public int hashCode() + { + return (stringValue == null ? 0 : stringValue.hashCode()); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj != null && obj instanceof PropertyStringValueEntity) + { + PropertyStringValueEntity that = (PropertyStringValueEntity) obj; + return EqualsHelper.nullSafeEquals(this.stringValue, that.stringValue); + } + else + { + return false; + } + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(512); + sb.append("PropertyStringValueEntity") + .append("[ ID=").append(id) + .append(", stringValue=").append(stringValue) + .append("]"); + return sb.toString(); + } + + public Pair getEntityPair() + { + if (stringValue != null && stringValue.equals(PropertyStringValueEntity.EMPTY_STRING_REPLACEMENT)) + { + return new Pair(id, PropertyStringValueEntity.EMPTY_STRING); + } + else + { + return new Pair(id, stringValue); + } + } + + /** + * Set the string and string-end values + */ + public void setValue(String value) + { + if (value == null) + { + throw new IllegalArgumentException("Null strings cannot be persisted"); + } + if (value != null && value.equals(PropertyStringValueEntity.EMPTY_STRING)) + { + // Oracle: We can't insert empty strings into the column. + value = PropertyStringValueEntity.EMPTY_STRING_REPLACEMENT; + } + stringValue = value; + // Calculate the crc value from the original value + Pair crcPair = CrcHelper.getStringCrcPair(value, 16, false, true); + stringEndLower = crcPair.getFirst(); + stringCrc = crcPair.getSecond(); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getStringValue() + { + return stringValue; + } + + public void setStringValue(String stringValue) + { + this.stringValue = stringValue; + } + + public String getStringEndLower() + { + return stringEndLower; + } + + public void setStringEndLower(String stringEndLower) + { + this.stringEndLower = stringEndLower; + } + + public Long getStringCrc() + { + return stringCrc; + } + + public void setStringCrc(Long stringCrc) + { + this.stringCrc = stringCrc; + } +} \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/service/cmr/audit/AuditService.java b/repository/src/main/java/org/alfresco/service/cmr/audit/AuditService.java index bbb2f886ac..a67bca619e 100644 --- a/repository/src/main/java/org/alfresco/service/cmr/audit/AuditService.java +++ b/repository/src/main/java/org/alfresco/service/cmr/audit/AuditService.java @@ -1,266 +1,267 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2024 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.service.cmr.audit; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * The public API by which applications can query the audit logs and enable or disable auditing. - * - * @author Derek Hulley - */ -public interface AuditService -{ - /** - * @return Returns true if auditing is globally enabled - * - * @since 3.4 - */ - boolean isAuditEnabled(); - - /** - * Enable or disable the global auditing state - * - * @param enable true to enable auditing globally or false to disable - * - * @since 3.4 - */ - void setAuditEnabled(boolean enable); - - /** - * Helper bean to carry information about an audit application. - * - * @author Derek Hulley - * @since 3.4 - */ - public static class AuditApplication - { - private final String name; - private final String key; - private final boolean enabled; - /** - * Constructor for final variables - */ - public AuditApplication(String name, String key, boolean enabled) - { - this.name = name; - this.key = key; - this.enabled = enabled; - } - public String getName() - { - return name; - } - public String getKey() - { - return key; - } - public boolean isEnabled() - { - return enabled; - } - } - - /** - * Get all registered audit applications - * - * @return Returns a map of audit applications keyed by their name - * - * @since 3.4 - */ - Map getAuditApplications(); - - /** - * @param applicationName the name of the application to check - * @param path the path to check - * @return Returns true if auditing is enabled for the given path - * - * @since 3.2 - */ - boolean isAuditEnabled(String applicationName, String path); - - /** - * Enable auditing for an application path - * - * @param applicationName the name of the application to check - * @param path the path to enable - * - * @since 3.2 - */ - void enableAudit(String applicationName, String path); - - /** - * Disable auditing for an application path - * - * @param applicationName the name of the application to check - * @param path the path to disable - * - * @since 3.2 - */ - void disableAudit(String applicationName, String path); - - /** - * Remove all audit entries for the given application - * - * @param applicationName the name of the application for which to remove entries - * @return Returns the number of audit entries deleted - * - * @since 3.2 - * - * @deprecated Use {@link #clearAudit(String, Long, Long)} - */ - int clearAudit(String applicationName); - - /** - * Remove audit entries for the given application between the time ranges. If no start - * time is given then entries are deleted as far back as they exist. If no end time is - * given then entries are deleted up until the current time. - * - * @param applicationName the name of the application for which to remove entries - * @param fromTime the start time of entries to remove (inclusive and optional) - * @param toTime the end time of entries to remove (exclusive and optional) - * @return Returns the number of audit entries deleted - * - * @since 3.4 - */ - int clearAudit(String applicationName, Long fromTime, Long toTime); - - /** - * Remove audit entries for the given application between the audit entry ids. - * - * @param applicationName the name of the application for which to remove entries - * @param fromId the start time of entries to remove (inclusive and optional) - * @param toId the end time of entries to remove (exclusive and optional) - * @return Returns the number of audit entries deleted - * - * @since 5.2.2 - */ - int clearAuditByIdRange(String applicationName, Long fromId, Long toId); - - /** - * Delete a discrete list of audit entries. - *

- * This method should not be called while processing - * {@link #auditQuery(AuditQueryCallback, AuditQueryParameters, int) query results}. - * - * @param auditEntryIds the IDs of all audit entries to delete - * @return Returns the number of audit entries deleted - * - * @since 3.4 - */ - int clearAudit(List auditEntryIds); - - /** - * The interface that will be used to give query results to the calling code. - * - * @since 3.2 - */ - public static interface AuditQueryCallback - { - /** - * Determines whether this callback requires the values argument to be populated when {@link #handleAuditEntry} - * is called. - * - * @return true if this callback requires the values argument to be populated - */ - boolean valuesRequired(); - - /** - * Handle a row of audit entry data. - * - * @param entryId the unique audit entry ID - * @param applicationName the name of the application - * @param user the user that logged the entry - * @param time the time of the entry - * @param values the values map as created - * @return Return true to continue processing rows or false to stop - */ - boolean handleAuditEntry( - Long entryId, - String applicationName, - String user, - long time, - Map values); - - /** - * Handle audit entry failures - * - * @param entryId the entry ID - * @param errorMsg the error message - * @param error the exception causing the error (may be null) - * @return Return true to continue processing rows or false to stop - */ - boolean handleAuditEntryError(Long entryId, String errorMsg, Throwable error); - } - - /** - * Issue an audit query using the given parameters and consuming results in the callback. - * Results are returned in entry order, corresponding to time order. - * - * @param callback the callback that will handle results - * @param parameters the parameters for the query (may not be null) - * @param maxResults the maximum number of results to retrieve (must be greater than 0) - * - * @throws IllegalArgumentException if maxResults less or equal to zero - * - * @since 3.3 - */ - void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults); - - /** - * Issue an audit query to retrieve min / max audit record id for a given application. - * - * @param applicationName the name of the application - * @param extremes a list containing min/max or both - * @return a map containing min/max and the associated value - */ - HashMap getAuditMinMaxByApp(String applicationName, List extremes); - - /** - * Issue an audit query to retrieve min / max audit record id for a given application. - * - * @param applicationName the name of the application - * @return a map containing min/max and the associated value - */ - default int getAuditEntriesCountByApp(String applicationName) - { - return -1; - } - - /** - * Issue an audit query to retrieve min / max audit record id for a given application and properties - * - * @param parameters audit parameters provided by the where clause on the ReST API - * @return a map containing min/max and the associated value - */ - default int getAuditEntriesCountByAppAndProperties(AuditQueryParameters parameters) - { - return -1; - } -} +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.service.cmr.audit; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * The public API by which applications can query the audit logs and enable or disable auditing. + * + * @author Derek Hulley + */ +public interface AuditService +{ + /** + * @return Returns true if auditing is globally enabled + * + * @since 3.4 + */ + boolean isAuditEnabled(); + + /** + * Enable or disable the global auditing state + * + * @param enable true to enable auditing globally or false to disable + * + * @since 3.4 + */ + void setAuditEnabled(boolean enable); + + /** + * Helper bean to carry information about an audit application. + * + * @author Derek Hulley + * @since 3.4 + */ + public static class AuditApplication + { + private final String name; + private final String key; + private final boolean enabled; + /** + * Constructor for final variables + */ + public AuditApplication(String name, String key, boolean enabled) + { + this.name = name; + this.key = key; + this.enabled = enabled; + } + public String getName() + { + return name; + } + public String getKey() + { + return key; + } + public boolean isEnabled() + { + return enabled; + } + } + + /** + * Get all registered audit applications + * + * @return Returns a map of audit applications keyed by their name + * + * @since 3.4 + */ + Map getAuditApplications(); + + /** + * @param applicationName the name of the application to check + * @param path the path to check + * @return Returns true if auditing is enabled for the given path + * + * @since 3.2 + */ + boolean isAuditEnabled(String applicationName, String path); + + /** + * Enable auditing for an application path + * + * @param applicationName the name of the application to check + * @param path the path to enable + * + * @since 3.2 + */ + void enableAudit(String applicationName, String path); + + /** + * Disable auditing for an application path + * + * @param applicationName the name of the application to check + * @param path the path to disable + * + * @since 3.2 + */ + void disableAudit(String applicationName, String path); + + /** + * Remove all audit entries for the given application + * + * @param applicationName the name of the application for which to remove entries + * @return Returns the number of audit entries deleted + * + * @since 3.2 + * + * @deprecated Use {@link #clearAudit(String, Long, Long)} + */ + int clearAudit(String applicationName); + + /** + * Remove audit entries for the given application between the time ranges. If no start + * time is given then entries are deleted as far back as they exist. If no end time is + * given then entries are deleted up until the current time. + * + * @param applicationName the name of the application for which to remove entries + * @param fromTime the start time of entries to remove (inclusive and optional) + * @param toTime the end time of entries to remove (exclusive and optional) + * @return Returns the number of audit entries deleted + * + * @since 3.4 + */ + int clearAudit(String applicationName, Long fromTime, Long toTime); + + /** + * Remove audit entries for the given application between the audit entry ids. + * + * @param applicationName the name of the application for which to remove entries + * @param fromId the start time of entries to remove (inclusive and optional) + * @param toId the end time of entries to remove (exclusive and optional) + * @return Returns the number of audit entries deleted + * + * @since 5.2.2 + */ + int clearAuditByIdRange(String applicationName, Long fromId, Long toId); + + /** + * Delete a discrete list of audit entries. + *

+ * This method should not be called while processing + * {@link #auditQuery(AuditQueryCallback, AuditQueryParameters, int) query results}. + * + * @param auditEntryIds the IDs of all audit entries to delete + * @return Returns the number of audit entries deleted + * + * @since 3.4 + */ + int clearAudit(List auditEntryIds); + + /** + * The interface that will be used to give query results to the calling code. + * + * @since 3.2 + */ + public static interface AuditQueryCallback + { + /** + * Determines whether this callback requires the values argument to be populated when {@link #handleAuditEntry} + * is called. + * + * @return true if this callback requires the values argument to be populated + */ + boolean valuesRequired(); + + /** + * Handle a row of audit entry data. + * + * @param entryId the unique audit entry ID + * @param applicationName the name of the application + * @param user the user that logged the entry + * @param time the time of the entry + * @param values the values map as created + * @return Return true to continue processing rows or false to stop + */ + boolean handleAuditEntry( + Long entryId, + String applicationName, + String user, + long time, + Map values); + + /** + * Handle audit entry failures + * + * @param entryId the entry ID + * @param errorMsg the error message + * @param error the exception causing the error (may be null) + * @return Return true to continue processing rows or false to stop + */ + boolean handleAuditEntryError(Long entryId, String errorMsg, Throwable error); + } + + /** + * Issue an audit query using the given parameters and consuming results in the callback. + * Results are returned in entry order, corresponding to time order. + * + * @param callback the callback that will handle results + * @param parameters the parameters for the query (may not be null) + * @param maxResults the maximum number of results to retrieve (must be greater than 0) + * + * @throws IllegalArgumentException if maxResults less or equal to zero + * + * @since 3.3 + */ + void auditQuery(AuditQueryCallback callback, AuditQueryParameters parameters, int maxResults); + + /** + * Issue an audit query to retrieve min / max audit record id for a given application. + * + * @param applicationName the name of the application + * @param extremes a list containing min/max or both + * @return a map containing min/max and the associated value + */ + HashMap getAuditMinMaxByApp(String applicationName, List extremes); + + /** + * Issue an audit query to retrieve min / max audit record id for a given application. + * + * @param applicationName the name of the application + * @return a map containing min/max and the associated value + */ + default int getAuditEntriesCountByApp(String applicationName) + { + return -1; + } + + /** + * Issue an audit query to retrieve min / max audit record id for a given application and properties + * + * @param applicationName the name of the application + * @param parameters audit parameters provided by the where clause on the ReST API + * @return a map containing min/max and the associated value + */ + default int getAuditEntriesCountByAppAndProperties(String applicationName, AuditQueryParameters parameters) + { + return -1; + } +} \ No newline at end of file