Java tutorial
/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 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 <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.repo.audit; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ArrayList; import java.util.Locale; 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.domain.schema.SchemaBootstrap; 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.cmr.repository.MLText; 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. * <p> * To turn on logging of all <i>potentially auditable</i> data, turn on logging for:<br/> * <strong>{@link #INBOUND_LOGGER org.alfresco.repo.audit.inbound}</strong>. * <p/> * 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) { 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 0; } Long applicationId = application.getApplicationId(); 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 3.2 */ @Override public int deleteAuditEntries(List<Long> 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<String> getDisabledPaths(AuditApplication application) { try { Long disabledPathsId = application.getDisabledPathsId(); Set<String> disabledPaths = (Set<String>) propertyValueDAO.getPropertyById(disabledPathsId); return new HashSet<String>(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<String, AuditApplication> getAuditApplications() { return auditModelRegistry.getAuditApplications(); } /** * {@inheritDoc} * <p/> * Note that if DEBUG is on for the the {@link #INBOUND_LOGGER}, then <tt>true</tt> * will always be returned. * * @since 3.2 */ public boolean areAuditValuesRequired() { return (loggerInbound.isDebugEnabled()) || (isAuditEnabled() && !auditModelRegistry.getAuditPathMapper().isEmpty()); } /** * {@inheritDoc} * <p/> * Note that if DEBUG is on for the the {@link #INBOUND_LOGGER}, then <tt>true</tt> * will always be returned. * * @since 3.4 */ @Override public boolean areAuditValuesRequired(String path) { PathMapper pathMapper = auditModelRegistry.getAuditPathMapper(); Set<String> 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<String> 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<String> disabledPaths = getDisabledPaths(application); // Remove any paths that start with the given path boolean changed = false; Iterator<String> 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<String> 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<String> 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<String, Serializable> recordAuditValues(String rootPath, Map<String, Serializable> values) { return recordAuditValuesWithUserFilter(rootPath, values, true); } protected <T> T trimStringsIfNecessary(T values) { T processed; if (values instanceof MLText) { // need to treat MLText first because it is actually a HashMap Map<Locale, String> localizedStrings = trimStringsIfNecessary((MLText) values); if (localizedStrings != values) { // processed so far is only defensive copy of a Map, not a MLText processed = (T) new MLText(); ((MLText) processed).putAll(localizedStrings); } else { // no changes processed = values; } } else if (values instanceof Map<?, ?>) { processed = (T) trimStringsIfNecessary((Map<?, ?>) values); } else if (values instanceof List<?>) { // need to treat list specially to preserve order processed = (T) trimStringsIfNecessary((List<?>) values); } else if (values instanceof Collection<?>) { // any other collection treated as unordered with no guarantee processed data will be in same order processed = (T) trimStringsIfNecessary((Collection<?>) values); } else if (values instanceof String) { processed = (T) SchemaBootstrap.trimStringForTextFields((String) values); } else { // don't know how to process processed = values; } return processed; } private <V> List<V> trimStringsIfNecessary(List<V> values) { List<V> processed = values; int idx = 0; for (V auditValue : values) { if (auditValue != null) { V processedAuditValue = trimStringsIfNecessary(auditValue); if (processedAuditValue != auditValue && !auditValue.equals(processedAuditValue)) { if (processed == values) { // defensive copy processed = new ArrayList<V>(values); } processed.set(idx, processedAuditValue); } } idx++; } return processed; } private <V> Collection<V> trimStringsIfNecessary(Collection<V> values) { Collection<V> processed = values; for (V auditValue : values) { if (auditValue != null) { V processedAuditValue = trimStringsIfNecessary(auditValue); if (processedAuditValue != auditValue && !auditValue.equals(processedAuditValue)) { if (processed == values) { // defensive copy processed = new HashSet<V>(values); } processed.remove(auditValue); processed.add(processedAuditValue); } } } return processed; } private <K, V> Map<K, V> trimStringsIfNecessary(Map<K, V> values) { Map<K, V> processed = values; for (Map.Entry<K, V> entry : values.entrySet()) { V auditValue = entry.getValue(); if (auditValue != null) { V processedAuditValue = trimStringsIfNecessary(auditValue); if (processedAuditValue != auditValue && !auditValue.equals(processedAuditValue)) { if (processed == values) { // defensive copy processed = new HashMap<K, V>(values); } processed.put(entry.getKey(), processedAuditValue); } } } return processed; } @Override public Map<String, Serializable> recordAuditValuesWithUserFilter(String rootPath, Map<String, Serializable> 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(); } // MNT-12196 values = trimStringsIfNecessary(values); // Log inbound values if (loggerInbound.isDebugEnabled()) { StringBuilder sb = new StringBuilder(values.size() * 64); sb.append("\n").append("Inbound audit values:"); for (Map.Entry<String, Serializable> 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<String, Serializable> pathedValues = new HashMap<String, Serializable>(values.size() * 2); for (Map.Entry<String, Serializable> 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<String, Serializable> 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<Map<String, Serializable>> callback = new RetryingTransactionCallback<Map<String, Serializable>>() { public Map<String, Serializable> 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<String, Serializable> recordAuditValuesImpl(Map<String, Serializable> mappedValues) { // Group the values by root path Map<String, Map<String, Serializable>> mappedValuesByRootKey = new HashMap<String, Map<String, Serializable>>(); for (Map.Entry<String, Serializable> entry : mappedValues.entrySet()) { String path = entry.getKey(); String rootKey = AuditApplication.getRootKey(path); Map<String, Serializable> rootKeyMappedValues = mappedValuesByRootKey.get(rootKey); if (rootKeyMappedValues == null) { rootKeyMappedValues = new HashMap<String, Serializable>(7); mappedValuesByRootKey.put(rootKey, rootKeyMappedValues); } rootKeyMappedValues.put(path, entry.getValue()); } Map<String, Serializable> allAuditedValues = new HashMap<String, Serializable>(mappedValues.size() * 2 + 1); // Now audit for each of the root keys for (Map.Entry<String, Map<String, Serializable>> entry : mappedValuesByRootKey.entrySet()) { String rootKey = entry.getKey(); Map<String, Serializable> 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<String> 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<String, Serializable> 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 <b>full paths</b>. * @return Returns all values as audited */ private Map<String, Serializable> audit(final AuditApplication application, Set<String> disabledPaths, final Map<String, Serializable> 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<String> generatorKeys = values.keySet(); // Eliminate any paths that have been disabled Iterator<String> 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<String, DataGenerator> generators = application.getDataGenerators(generatorKeys); Map<String, Serializable> auditData = generateData(generators); // Now extract values Map<String, Serializable> extractedData = AuthenticationUtil .runAs(new RunAsWork<Map<String, Serializable>>() { public Map<String, Serializable> 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<String, Serializable> value : auditData.entrySet()) { String root = value.getKey(); int index = root.lastIndexOf("/"); Map<String, Serializable> argc = new HashMap<String, Serializable>(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<String, Serializable> entry : values.entrySet()) { sb.append("\t\t").append(entry).append("\n"); } sb.append("\n\tAudit Data: \n"); for (Map.Entry<String, Serializable> 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<String, Serializable> 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<String, Serializable> extractData(AuditApplication application, Map<String, Serializable> values) { Map<String, Serializable> newData = new HashMap<String, Serializable>(values.size()); List<DataExtractorDefinition> 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<String, Serializable> entry : values.entrySet()) { sb.append("\t\t").append(entry).append("\n"); } sb.append("\n\tNew Data: \n"); for (Map.Entry<String, Serializable> 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<String, Serializable> generateData(Map<String, DataGenerator> generators) { Map<String, Serializable> newData = new HashMap<String, Serializable>(generators.size() + 5); for (Map.Entry<String, DataGenerator> 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); } }