diff --git a/config/alfresco/audit-services-context.xml b/config/alfresco/audit-services-context.xml index 2c350a68c8..89b78ba79e 100644 --- a/config/alfresco/audit-services-context.xml +++ b/config/alfresco/audit-services-context.xml @@ -79,6 +79,7 @@ + diff --git a/config/alfresco/audit/alfresco-audit-3.2.xsd b/config/alfresco/audit/alfresco-audit-3.2.xsd index db39e32c93..85125894f7 100644 --- a/config/alfresco/audit/alfresco-audit-3.2.xsd +++ b/config/alfresco/audit/alfresco-audit-3.2.xsd @@ -57,7 +57,6 @@ - @@ -66,12 +65,6 @@ - - - - - - @@ -84,6 +77,7 @@ + @@ -108,7 +102,7 @@ - + @@ -118,4 +112,12 @@ + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/audit/alfresco-audit-repository.xml b/config/alfresco/audit/alfresco-audit-repository.xml index b9f17ec74d..40e7f3311c 100644 --- a/config/alfresco/audit/alfresco-audit-repository.xml +++ b/config/alfresco/audit/alfresco-audit-repository.xml @@ -23,12 +23,10 @@ - - - - + + - + @@ -39,10 +37,12 @@ + diff --git a/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java b/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java index 6ea1b4a305..d03f546c1a 100644 --- a/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java +++ b/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java @@ -24,11 +24,20 @@ */ package org.alfresco.repo.audit; +import java.net.URL; +import java.util.Map; + import junit.framework.TestCase; +import org.alfresco.repo.audit.extractor.DataExtractor; +import org.alfresco.repo.audit.generator.DataGenerator; +import org.alfresco.repo.audit.generator.DataGenerator.DataGeneratorScope; +import org.alfresco.repo.audit.model.AuditApplication; +import org.alfresco.repo.audit.model.AuditModelException; import org.alfresco.repo.audit.model.AuditModelRegistry; import org.alfresco.util.ApplicationContextHelper; import org.springframework.context.ApplicationContext; +import org.springframework.util.ResourceUtils; /** * Tests that auditing is loaded properly on repository startup. @@ -40,7 +49,7 @@ import org.springframework.context.ApplicationContext; */ public class AuditBootstrapTest extends TestCase { - private static final String APPLICATION_REPOSITORY = "Alfresco Repository"; + private static final String APPLICATION_TEST = "Alfresco Test"; private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); @@ -50,6 +59,11 @@ public class AuditBootstrapTest extends TestCase public void setUp() throws Exception { auditModelRegistry = (AuditModelRegistry) ctx.getBean("auditModel.registry"); + + // Register a new model + URL testModelUrl = ResourceUtils.getURL("classpath:alfresco/audit/alfresco-audit-test.xml"); + auditModelRegistry.registerModel(testModelUrl); + auditModelRegistry.loadAuditModels(); } public void testSetUp() @@ -57,9 +71,116 @@ public class AuditBootstrapTest extends TestCase // Just here to fail if the basic startup fails } + private void loadBadModel(String url) throws Exception + { + try + { + URL testModelUrl = ResourceUtils.getURL(url); + auditModelRegistry.registerModel(testModelUrl); + auditModelRegistry.loadAuditModels(); + fail("Expected model loading to fail."); + } + catch (AuditModelException e) + { + // Expected + } + } + + public void testModelLoading_NoDataExtractor() throws Exception + { + loadBadModel("classpath:alfresco/audit/alfresco-audit-test-bad-01.xml"); + } + + public void testModelLoading_NoDataGenerator() throws Exception + { + loadBadModel("classpath:alfresco/audit/alfresco-audit-test-bad-02.xml"); + } + + public void testModelLoading_DuplicatePath() throws Exception + { + loadBadModel("classpath:alfresco/audit/alfresco-audit-test-bad-03.xml"); + } + + public void testModelLoading_UppercasePath() throws Exception + { + loadBadModel("classpath:alfresco/audit/alfresco-audit-test-bad-04.xml"); + } + + public void testModelLoading_InvalidScope() throws Exception + { + loadBadModel("classpath:alfresco/audit/alfresco-audit-test-bad-05.xml"); + } + public void testGetModelId() { - Long repoId = auditModelRegistry.getAuditModelId(APPLICATION_REPOSITORY); - assertNotNull("No audit model ID for " + APPLICATION_REPOSITORY, repoId); + Long repoId = auditModelRegistry.getAuditModelId(APPLICATION_TEST); + assertNotNull("No audit model ID for " + APPLICATION_TEST, repoId); + } + + private void testBadPath(AuditApplication app, String path) + { + try + { + app.checkPath(path); + fail("Expected path check to fail."); + } + catch (AuditModelException e) + { + // Expected + } + } + + public void testAuditApplication_Path() + { + AuditApplication app = auditModelRegistry.getAuditApplication(APPLICATION_TEST); + assertNotNull(app); + + // Check that path checks are working + testBadPath(app, null); + testBadPath(app, ""); + testBadPath(app, "test"); + testBadPath(app, "/Test"); + testBadPath(app, "/test/"); + } + + public void testAuditApplication_GetDataExtractors() + { + AuditApplication app = auditModelRegistry.getAuditApplication(APPLICATION_TEST); + assertNotNull(app); + + Map extractors = app.getDataExtractors("/blah"); + assertNotNull("Should never get a null map", extractors); + assertTrue("Expected no extractors", extractors.isEmpty()); + + extractors = app.getDataExtractors("/test/1.1/2.1/3.1/4.1"); + assertEquals(2, extractors.size()); + assertTrue(extractors.containsKey("/test/1.1/2.1/3.1/value.1")); + assertTrue(extractors.containsKey("/test/1.1/2.1/3.1/4.1/value.1")); + } + + public void testAuditApplication_GetDataGenerators_AnyScope() + { + AuditApplication app = auditModelRegistry.getAuditApplication(APPLICATION_TEST); + assertNotNull(app); + + Map generators = app.getDataGenerators("/blah", DataGeneratorScope.ALL); + assertNotNull("Should never get a null map", generators); + assertTrue("Expected no generators", generators.isEmpty()); + + generators = app.getDataGenerators("/test/1.1/2.1/3.1/4.1", DataGeneratorScope.ALL); + assertEquals(1, generators.size()); + assertTrue(generators.containsKey("/test/time")); + + generators = app.getDataGenerators("/test/1.1/2.1/3.1/4.1", DataGeneratorScope.SESSION); + assertEquals(1, generators.size()); + assertTrue(generators.containsKey("/test/time")); + + generators = app.getDataGenerators("/test/1.1/2.1/3.1/4.1", DataGeneratorScope.AUDIT); + assertEquals(0, generators.size()); + + generators = app.getDataGenerators("/test/1.1/2.2/3.2/4.1", DataGeneratorScope.ALL); + assertEquals(2, generators.size()); + assertTrue(generators.containsKey("/test/time")); + assertTrue(generators.containsKey("/test/1.1/2.2/3.2/4.1/time")); } } diff --git a/source/java/org/alfresco/repo/audit/AuditComponent.java b/source/java/org/alfresco/repo/audit/AuditComponent.java index 8ea6bec188..908fef34fb 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponent.java +++ b/source/java/org/alfresco/repo/audit/AuditComponent.java @@ -91,14 +91,14 @@ public interface AuditComponent * * @param application the name of the application to log against * @param rootPath a base path of {@link AuditPath} key entries concatenated with . (period) - * @return Returns the unique session identifier + * @return Returns the unique session */ - public Long startAuditSession(String application, String rootPath); + public AuditSession startAuditSession(String application, String rootPath); /** * Record a set of values against the given session. * - * @param sessionId a pre-existing audit session to continue with + * @param session a pre-existing audit session to continue with * @param values the values to audit mapped by {@link AuditPath} key relative to the session * root path * @@ -106,5 +106,5 @@ public interface AuditComponent * * @since 3.2 */ - public void audit(Long sessionId, Map values); + public void audit(AuditSession session, Map values); } diff --git a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java index f604b79c47..5f859bf60d 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java +++ b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java @@ -32,14 +32,13 @@ import java.util.Date; import java.util.List; import java.util.Map; +import org.alfresco.repo.audit.model.AuditApplication; import org.alfresco.repo.audit.model.AuditEntry; import org.alfresco.repo.audit.model.AuditModelRegistry; import org.alfresco.repo.audit.model.TrueFalseUnset; -import org.alfresco.repo.audit.model._3.Application; import org.alfresco.repo.domain.audit.AuditDAO; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.Auditable; @@ -752,8 +751,7 @@ public class AuditComponentImpl implements AuditComponent * V3.2 from here on. Put all fixes to the older audit code before this point, please. */ - private static final Long NO_AUDIT_SESSION = new Long(-1); - private static final String KEY_AUDIT_SESSION = "Audit.Sessions"; + private static final AuditSession NO_AUDIT_SESSION = new AuditSession(); private AuditModelRegistry auditModelRegistry; @@ -766,13 +764,10 @@ public class AuditComponentImpl implements AuditComponent this.auditModelRegistry = auditModelRegistry; } - public Long startAuditSession(String applicationName, String rootPath) + public AuditSession startAuditSession(String applicationName, String rootPath) { - // First check that we can store a session on the transaction - Map sessionsById = TransactionalResourceHelper.getMap(KEY_AUDIT_SESSION); - // Get the application - Application application = auditModelRegistry.getAuditApplication(applicationName); + AuditApplication application = auditModelRegistry.getAuditApplication(applicationName); if (application == null) { if (logger.isDebugEnabled()) @@ -788,25 +783,34 @@ public class AuditComponentImpl implements AuditComponent throw new AuditException("No model exists for audit application: " + applicationName); } - // TODO: Validate root path against application + // Validate root path against application + String appRootKey = application.getApplicationKey() + AuditApplication.AUDIT_PATH_SEPARATOR; + if (rootPath == null || !rootPath.startsWith(appRootKey)) + { + throw new AuditException( + "An audit session's root path must start with the application's root key.\n" + + " Application: " + application.getApplicationName() + "\n" + + " Root key: " + application.getApplicationKey() + "\n" + + " Given root: " + rootPath); + } + // TODO: Pull out session properties and persist // Now create the session Long sessionId = auditDAO.createAuditSession(modelId, applicationName); // Create the session info and store it on the transaction AuditSession session = new AuditSession(application, rootPath, sessionId); - sessionsById.put(sessionId, session); // Done if (logger.isDebugEnabled()) { logger.debug("New audit session: " + session); } - return sessionId; + return session; } - public void audit(Long sessionId, Map values) + public void audit(AuditSession session, Map values) { - if (sessionId == NO_AUDIT_SESSION) + if (session == NO_AUDIT_SESSION) { // For some reason, the session was not to be used return; diff --git a/source/java/org/alfresco/repo/audit/AuditSession.java b/source/java/org/alfresco/repo/audit/AuditSession.java index 690ddb5a9a..4245c473d7 100644 --- a/source/java/org/alfresco/repo/audit/AuditSession.java +++ b/source/java/org/alfresco/repo/audit/AuditSession.java @@ -24,7 +24,7 @@ */ package org.alfresco.repo.audit; -import org.alfresco.repo.audit.model._3.Application; +import org.alfresco.repo.audit.model.AuditApplication; import org.alfresco.util.ParameterCheck; /** @@ -35,16 +35,26 @@ import org.alfresco.util.ParameterCheck; */ public class AuditSession { - private final Application application; + private final AuditApplication application; private final String rootPath; private final Long sessionId; + /** + * Constructor used to denote a dummy (no-audit) session + */ + /* package */ AuditSession() + { + application = null; + rootPath = null; + sessionId = null; + } + /** * @param application the audit application config being used * @param rootPath the root path being used for the session * @param sessionId the ID produced for the persisted session */ - public AuditSession(Application application, String rootPath, Long sessionId) + public AuditSession(AuditApplication application, String rootPath, Long sessionId) { ParameterCheck.mandatory("application", application); ParameterCheck.mandatoryString("rootPath", rootPath); @@ -58,7 +68,7 @@ public class AuditSession @Override public int hashCode() { - return (application.getName().hashCode() + rootPath.hashCode()); + return (application.hashCode() + rootPath.hashCode()); } @Override @@ -71,7 +81,7 @@ public class AuditSession else if (obj instanceof AuditSession) { AuditSession that = (AuditSession) obj; - return this.application.getName().equals(that.application.getName()) && + return this.application.equals(that.application) && this.rootPath.equals(that.rootPath); } else @@ -85,14 +95,14 @@ public class AuditSession { StringBuilder sb = new StringBuilder(512); sb.append("AuditSession") - .append("[ application=").append(application.getName()) + .append("[ application=").append(application) .append(", rootPath=").append(rootPath) .append(", sessionId=").append(sessionId) .append("]"); return sb.toString(); } - public Application getApplication() + public AuditApplication getApplication() { return application; } diff --git a/source/java/org/alfresco/repo/audit/extractor/AbstractDataExtractor.java b/source/java/org/alfresco/repo/audit/extractor/AbstractDataExtractor.java new file mode 100644 index 0000000000..3c46ebcb4f --- /dev/null +++ b/source/java/org/alfresco/repo/audit/extractor/AbstractDataExtractor.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.audit.extractor; + +/** + * Abstract implementation to provide support. + * + * @author Derek Hulley + * @since 3.2 + */ +public abstract class AbstractDataExtractor implements DataExtractor +{ + /** + * This implementation assumes all extractors are stateless i.e. if the class matches + * then the instances are equal. + */ + @Override + public boolean equals(Object obj) + { + if (obj != null && obj.getClass().equals(this.getClass())) + { + return true; + } + else + { + return false; + } + } +} diff --git a/source/java/org/alfresco/repo/audit/extractor/SimpleValueDataExtractor.java b/source/java/org/alfresco/repo/audit/extractor/SimpleValueDataExtractor.java index 0cd2e93b73..2dad029e5f 100644 --- a/source/java/org/alfresco/repo/audit/extractor/SimpleValueDataExtractor.java +++ b/source/java/org/alfresco/repo/audit/extractor/SimpleValueDataExtractor.java @@ -32,7 +32,7 @@ package org.alfresco.repo.audit.extractor; * @author Derek Hulley * @since 3.2 */ -public class SimpleValueDataExtractor implements DataExtractor +public class SimpleValueDataExtractor extends AbstractDataExtractor { /** * @return Returns true always diff --git a/source/java/org/alfresco/repo/audit/generator/AbstractDataGenerator.java b/source/java/org/alfresco/repo/audit/generator/AbstractDataGenerator.java new file mode 100644 index 0000000000..8b96127dc6 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/generator/AbstractDataGenerator.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.audit.generator; + +/** + * Abstract implementation to provide support. + * + * @author Derek Hulley + * @since 3.2 + */ +public abstract class AbstractDataGenerator implements DataGenerator +{ + /** + * This implementation assumes all generators are stateless i.e. if the class matches + * then the instances are equal. + */ + @Override + public boolean equals(Object obj) + { + if (obj != null && obj.getClass().equals(this.getClass())) + { + return true; + } + else + { + return false; + } + } +} diff --git a/source/java/org/alfresco/repo/audit/generator/AuthenticatedUserDataGenerator.java b/source/java/org/alfresco/repo/audit/generator/AuthenticatedUserDataGenerator.java index 1f7de8ee14..4e94ec1d67 100644 --- a/source/java/org/alfresco/repo/audit/generator/AuthenticatedUserDataGenerator.java +++ b/source/java/org/alfresco/repo/audit/generator/AuthenticatedUserDataGenerator.java @@ -32,7 +32,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; * @author Derek Hulley * @since 3.2 */ -public class AuthenticatedUserDataGenerator implements DataGenerator +public class AuthenticatedUserDataGenerator extends AbstractDataGenerator { /** * @return Returns the currently-authenticated user diff --git a/source/java/org/alfresco/repo/audit/generator/DataGenerator.java b/source/java/org/alfresco/repo/audit/generator/DataGenerator.java index f1933d5074..7a14bb0fa4 100644 --- a/source/java/org/alfresco/repo/audit/generator/DataGenerator.java +++ b/source/java/org/alfresco/repo/audit/generator/DataGenerator.java @@ -36,6 +36,16 @@ package org.alfresco.repo.audit.generator; */ public interface DataGenerator { + public static enum DataGeneratorScope + { + /** Data is generated for new audit sessions only */ + SESSION, + /** Data is generated for individual audit calls only */ + AUDIT, + /** Data is generated for new audit sessions and audit calls */ + ALL + } + /** * Get the data generated by the instance. * diff --git a/source/java/org/alfresco/repo/audit/generator/SystemTimeDataGenerator.java b/source/java/org/alfresco/repo/audit/generator/SystemTimeDataGenerator.java index 698a5c2444..6815b2cfa0 100644 --- a/source/java/org/alfresco/repo/audit/generator/SystemTimeDataGenerator.java +++ b/source/java/org/alfresco/repo/audit/generator/SystemTimeDataGenerator.java @@ -32,7 +32,7 @@ import java.util.Date; * @author Derek Hulley * @since 3.2 */ -public class SystemTimeDataGenerator implements DataGenerator +public class SystemTimeDataGenerator extends AbstractDataGenerator { /** * @return Returns the current time diff --git a/source/java/org/alfresco/repo/audit/generator/TransactionIdDataGenerator.java b/source/java/org/alfresco/repo/audit/generator/TransactionIdDataGenerator.java index ade808b765..6ce1eac89a 100644 --- a/source/java/org/alfresco/repo/audit/generator/TransactionIdDataGenerator.java +++ b/source/java/org/alfresco/repo/audit/generator/TransactionIdDataGenerator.java @@ -32,7 +32,7 @@ import org.alfresco.repo.transaction.AlfrescoTransactionSupport; * @author Derek Hulley * @since 3.2 */ -public class TransactionIdDataGenerator implements DataGenerator +public class TransactionIdDataGenerator extends AbstractDataGenerator { /** * @return Returns the current transaction ID (null if not in a transction) diff --git a/source/java/org/alfresco/repo/audit/model/AuditApplication.java b/source/java/org/alfresco/repo/audit/model/AuditApplication.java new file mode 100644 index 0000000000..5b7a5376c8 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/AuditApplication.java @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing + */ +package org.alfresco.repo.audit.model; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.audit.extractor.DataExtractor; +import org.alfresco.repo.audit.generator.DataGenerator; +import org.alfresco.repo.audit.generator.DataGenerator.DataGeneratorScope; +import org.alfresco.repo.audit.model._3.Application; +import org.alfresco.repo.audit.model._3.AuditPath; +import org.alfresco.repo.audit.model._3.GenerateValue; +import org.alfresco.repo.audit.model._3.RecordValue; +import org.alfresco.repo.audit.model._3.ScopeAttribute; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Helper class that wraps the {@link Application audit application}. + * Once wrapped, client code doesn't need access to any of the generated + * model-driven classes. + * + * @author Derek Hulley + * @since 3.2 + */ +public class AuditApplication +{ + public static final String AUDIT_PATH_SEPARATOR = "/"; + + private static final Log logger = LogFactory.getLog(AuditApplication.class); + + private final String applicationName; + private final String applicationKey; + + private final Map dataExtractorsByName; + private final Map dataGeneratorsByName; + @SuppressWarnings("unused") + private final Application application; + + /** Derived expaned map for fast lookup */ + private Map> dataExtractors = new HashMap>(11); + /** Derived expaned map for fast lookup */ + private Map> dataGenerators = new HashMap>(11); + /** Derived expaned map for fast lookup */ + private Map dataGeneratorScopes = new HashMap(11); + + /** + * @param application the application that will be wrapped + * @param dataExtractorsByName data extractors to use + * @param dataGeneratorsByName data generators to use + */ + /* package */ AuditApplication( + Map dataExtractorsByName, + Map dataGeneratorsByName, + Application application) + { + this.dataExtractorsByName = dataExtractorsByName; + this.dataGeneratorsByName = dataGeneratorsByName; + this.application = application; + + this.applicationName = application.getName(); + this.applicationKey = application.getKey(); + + buildAuditPaths(application); + } + + @Override + public int hashCode() + { + return applicationName.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj instanceof AuditApplication) + { + AuditApplication that = (AuditApplication) obj; + return this.applicationName.equals(that.applicationName); + } + else + { + return false; + } + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(56); + sb.append("AuditApplication") + .append("[ name=").append(applicationName) + .append("]"); + return sb.toString(); + } + + /** + * Get the application name + */ + public String getApplicationName() + { + return applicationName; + } + + /** + * Get the key (root path) for the application + */ + public String getApplicationKey() + { + return applicationKey; + } + + /** + * Helper method to check that a path is correct for this application instance + * + * @param path the path in format /app-key/x/y/z + * @throws AuditModelException if the path is invalid + */ + public void checkPath(String path) + { + if (path == null || path.length() == 0) + { + generateException(path, "Invalid audit path."); + } + if (!path.startsWith(AUDIT_PATH_SEPARATOR)) + { + generateException( + path, + "An audit path must always start with the separator '" + AUDIT_PATH_SEPARATOR + "'."); + } + if (path.indexOf(applicationKey, 0) != 1) + { + generateException( + path, + "An audit path's first element must be the application's key i.e. '" + applicationKey + "'."); + } + if (path.endsWith(AUDIT_PATH_SEPARATOR)) + { + generateException( + path, + "An audit path may not end with the separator '" + AUDIT_PATH_SEPARATOR + "'."); + } + if (!path.toLowerCase().equals(path)) + { + generateException( + path, + "An audit path may only contain lowercase letters, '-' and '.' i.e. regex ([a-z]|[0-9]|\\-|\\.)*"); + } + } + + /** + * Get all data extractors applicable to a given path and scope. + * + * @param path the audit path + * @return Returns all data extractors mapped to their key-path + */ + public Map getDataExtractors(String path) + { + Map extractors = dataExtractors.get(path); + if (extractors == null) + { + // Don't give back a null + extractors = Collections.emptyMap(); + } + else + { + // we don't want to give back a modifiable map + extractors = Collections.unmodifiableMap(extractors); + } + + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Looked up data extractors: \n" + + " Path: " + path + "\n" + + " Found: " + extractors); + } + return extractors; + } + + /** + * Get all data generators applicable to a given path and scope. + * + * @param path the audit path + * @param scope the audit scope (e.g. SESSION or AUDIT) + * @return Returns all data generators mapped to their key-path + */ + public Map getDataGenerators(String path, DataGeneratorScope scope) + { + Map generators = dataGenerators.get(path); + if (generators == null) + { + // Don't give back a null + generators = new HashMap(0); + } + else + { + // Copy the map so that (a) we can modify it during iteration and (b) we return + // something that the client can't mess up. + generators = new HashMap(generators); + } + + if (scope != DataGeneratorScope.ALL) + { + // Go through them and eliminate the ones in the wrong scope + Iterator> iterator = generators.entrySet().iterator(); + while (iterator.hasNext()) + { + Map.Entry entry = iterator.next(); + String generatorPath = entry.getKey(); + DataGeneratorScope generatorScope = dataGeneratorScopes.get(generatorPath); + if (generatorScope == DataGeneratorScope.ALL) + { + // This one always applies + continue; + } + else if (generatorScope != scope) + { + // Wrong scope + iterator.remove(); + } + } + } + + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Looked up data generators: \n" + + " Path: " + path + "\n" + + " Scope: " + scope + "\n" + + " Found: " + generators); + } + return generators; + } + + /** + * Internal helper method to kick off generator and extractor path mappings + */ + private void buildAuditPaths(AuditPath auditPath) + { + buildAuditPaths( + auditPath, + "", + new HashSet(37), + new HashMap(13), + new HashMap(13)); + } + /** + * Recursive method to build generator and extractor mappings + */ + private void buildAuditPaths( + AuditPath auditPath, + String currentPath, + Set existingPaths, + Map upperExtractorsByPath, + Map upperGeneratorsByPath) + { + // Clone the upper maps to prevent pollution + upperExtractorsByPath = new HashMap(upperExtractorsByPath); + upperGeneratorsByPath = new HashMap(upperGeneratorsByPath); + + // Append the current audit path to the current path + currentPath = (currentPath + AUDIT_PATH_SEPARATOR + auditPath.getKey()); + // Make sure we have not processed it before + if (!existingPaths.add(currentPath)) + { + generateException(currentPath, "The audit path already exists."); + } + // Make sure that the path is all lowercase + checkPathCase(currentPath); + + // Get the data extractors declared for this key + for (RecordValue element : auditPath.getRecordValue()) + { + String key = element.getKey(); + String extractorPath = (currentPath + AUDIT_PATH_SEPARATOR + key); + if (!existingPaths.add(extractorPath)) + { + generateException(extractorPath, "The audit path already exists."); + } + // Make sure that the path is all lowercase + checkPathCase(extractorPath); + + String extractorName = element.getDataExtractor(); + DataExtractor extractor = dataExtractorsByName.get(extractorName); + if (extractor == null) + { + generateException(extractorPath, "No data extractor exists for name: " + extractorName); + } + // All generators that occur earlier in the path will also be applicable here + upperExtractorsByPath.put(extractorPath, extractor); + } + // All the extractors apply to the current path + dataExtractors.put(currentPath, upperExtractorsByPath); + + // Get the data generators declared for this key + for (GenerateValue element : auditPath.getGenerateValue()) + { + String key = element.getKey(); + String generatorPath = (currentPath + AUDIT_PATH_SEPARATOR + key); + if (!existingPaths.add(generatorPath)) + { + generateException(generatorPath, "The audit path already exists."); + } + // Make sure that the path is all lowercase + checkPathCase(generatorPath); + + String generatorName = element.getDataGenerator(); + DataGenerator generator = dataGeneratorsByName.get(generatorName); + if (generator == null) + { + generateException(generatorPath, "No data generator exists for name: " + generatorName); + } + // Store the scope + ScopeAttribute scopeAttribute = element.getScope(); + if (scopeAttribute == null) + { + generateException(generatorPath, "No scope defined for generator: " + generatorName); + } + String scopeStr = scopeAttribute.value(); + if (scopeStr == null) + { + scopeStr = DataGeneratorScope.AUDIT.toString(); + } + try + { + DataGeneratorScope scope = DataGeneratorScope.valueOf(scopeStr); + dataGeneratorScopes.put(generatorPath, scope); + } + catch (Throwable e) + { + generateException(generatorPath, "Illegal generator scope value: " + scopeStr); + } + // All generators that occur earlier in the path will also be applicable here + upperGeneratorsByPath.put(generatorPath, generator); + } + // All the generators apply to the current path + dataGenerators.put(currentPath, upperGeneratorsByPath); + + // Find all sub audit paths and recurse + for (AuditPath element : auditPath.getAuditPath()) + { + buildAuditPaths(element, currentPath, existingPaths, upperExtractorsByPath, upperGeneratorsByPath); + } + } + + private void checkPathCase(String path) + { + if (!path.equals(path.toLowerCase())) + { + generateException(path, "Audit key entries may only be in lowercase to ensure case-insensitivity."); + } + } + + private void generateException(String path, String msg) throws AuditModelException + { + throw new AuditModelException("" + + msg + "\n" + + " Application: " + applicationName + "\n" + + " Path: " + path); + } +} diff --git a/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java b/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java index 738835aa6e..d671a6a075 100644 --- a/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java +++ b/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java @@ -25,8 +25,6 @@ package org.alfresco.repo.audit.model; import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -41,16 +39,29 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.Unmarshaller; +import javax.xml.bind.ValidationEvent; +import javax.xml.bind.ValidationEventHandler; +import javax.xml.bind.ValidationEventLocator; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.error.ExceptionStackUtil; import org.alfresco.repo.audit.extractor.DataExtractor; import org.alfresco.repo.audit.generator.DataGenerator; import org.alfresco.repo.audit.model._3.Application; import org.alfresco.repo.audit.model._3.Audit; import org.alfresco.repo.audit.model._3.DataExtractors; import org.alfresco.repo.audit.model._3.DataGenerators; +import org.alfresco.repo.audit.model._3.ObjectFactory; import org.alfresco.repo.domain.audit.AuditDAO; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.util.ResourceUtils; +import org.xml.sax.SAXParseException; /** * Component used to store audit model definitions. It ensures that duplicate application and converter @@ -61,8 +72,13 @@ import org.alfresco.service.cmr.repository.NodeRef; */ public class AuditModelRegistry { + public static final String AUDIT_SCHEMA_LOCATION = "classpath:alfresco/audit/alfresco-audit-3.2.xsd"; + + private TransactionService transactionService; private AuditDAO auditDAO; + private static final Log logger = LogFactory.getLog(AuditModelRegistry.class); + private final ReentrantReadWriteLock.ReadLock readLock; private final ReentrantReadWriteLock.WriteLock writeLock; @@ -73,7 +89,7 @@ public class AuditModelRegistry /** * Used to lookup the audit application java hierarchy */ - private final Map auditApplicationsByName; + private final Map auditApplicationsByName; /** * Used to lookup a reference to the persisted config binary for an application */ @@ -92,10 +108,18 @@ public class AuditModelRegistry auditModels = new ArrayList(7); dataExtractorsByName = new HashMap(13); dataGeneratorsByName = new HashMap(13); - auditApplicationsByName = new HashMap(7); + auditApplicationsByName = new HashMap(7); auditModelIdsByApplicationsName = new HashMap(7); } + /** + * Service to ensure DAO calls are transactionally wrapped + */ + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + /** * Set the DAO used to persisted the registered audit models */ @@ -116,8 +140,7 @@ public class AuditModelRegistry { if (auditModelUrls.contains(auditModelUrl)) { - throw new AlfrescoRuntimeException( - "An audit model has already been registered at URL " + auditModelUrl); + logger.warn("An audit model has already been registered at URL " + auditModelUrl); } auditModelUrls.add(auditModelUrl); } @@ -145,26 +168,67 @@ public class AuditModelRegistry } } + /** + * Cleans out all derived data + */ + private void clearCaches() + { + auditModels.clear(); + dataExtractorsByName.clear(); + dataGeneratorsByName.clear();; + auditApplicationsByName.clear(); + auditModelIdsByApplicationsName.clear(); + } + /** * Method to load audit models into memory. This method is also responsible for persisting * the audit models for later retrieval. Models are loaded from the locations given by the * {@link #registerModel(URL) register} methods. + *

+ * Note, the models are loaded in a new transaction. */ public void loadAuditModels() { + RetryingTransactionCallback loadModelsCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Load models from the URLs + Set auditModelUrlsInner = new HashSet(auditModelUrls); + for (URL auditModelUrl : auditModelUrlsInner) + { + Audit audit = AuditModelRegistry.unmarshallModel(auditModelUrl); + // That worked, so now get an input stream and write the model + Long auditModelId = auditDAO.getOrCreateAuditModel(auditModelUrl).getFirst(); + try + { + // Now cache it (eagerly) + cacheAuditElements(auditModelId, audit); + } + catch (Throwable e) + { + // Mainly for test purposes, we clear out the failed URL + auditModelUrls.remove(auditModelUrl); + clearCaches(); + + throw new AuditModelException( + "Failed to load audit model: " + auditModelUrl + "\n" + + e.getMessage()); + } + } + // NOTE: If we support other types of loading, then that will have to go here, too + + // Done + return null; + } + }; + writeLock.lock(); + // Drop all cached data + clearCaches(); try { - // Load models from the URLs - for (URL auditModelUrl : auditModelUrls) - { - Audit audit = AuditModelRegistry.unmarshallModel(auditModelUrl); - // That worked, so now get an input stream and write the model - Long auditModelId = auditDAO.getOrCreateAuditModel(auditModelUrl).getFirst(); - // Now cache it (eagerly) - cacheAuditElements(auditModelId, audit); - } - // NOTE: If we support other types of loading, then that will have to go here, too + transactionService.getRetryingTransactionHelper().doInTransaction(loadModelsCallback); } finally { @@ -197,7 +261,7 @@ public class AuditModelRegistry * @param application the name of the audited application * @return the java model (null if not found) */ - public Application getAuditApplication(String application) + public AuditApplication getAuditApplication(String application) { readLock.lock(); try @@ -219,14 +283,8 @@ public class AuditModelRegistry { try { - File file = new File(configUrl.getFile()); - if (!file.exists()) - { - throw new AlfrescoRuntimeException("The Audit model XML was not found: " + configUrl); - } - // Load it - InputStream is = new BufferedInputStream(new FileInputStream(file)); + InputStream is = new BufferedInputStream(configUrl.openStream()); return unmarshallModel(is, configUrl.toString()); } catch (IOException e) @@ -238,25 +296,61 @@ public class AuditModelRegistry /** * Unmarshalls the Audit model from a stream */ - private static Audit unmarshallModel(InputStream is, String source) + private static Audit unmarshallModel(InputStream is, final String source) { + final Schema schema; + final JAXBContext jaxbCtx; + final Unmarshaller jaxbUnmarshaller; try { - JAXBContext jaxbCtx = JAXBContext.newInstance("org.alfresco.repo.audit.model._3"); - Unmarshaller jaxbUnmarshaller = jaxbCtx.createUnmarshaller(); + SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI); + schema = sf.newSchema(ResourceUtils.getURL(AUDIT_SCHEMA_LOCATION)); + jaxbCtx = JAXBContext.newInstance("org.alfresco.repo.audit.model._3"); + jaxbUnmarshaller = jaxbCtx.createUnmarshaller(); + jaxbUnmarshaller.setSchema(schema); + jaxbUnmarshaller.setEventHandler(new ValidationEventHandler() + { + public boolean handleEvent(ValidationEvent ve) + { + if (ve.getSeverity() == ValidationEvent.FATAL_ERROR || ve.getSeverity() == ValidationEvent.ERROR) + { + ValidationEventLocator locator = ve.getLocator(); + logger.error("Invalid Audit XML: \n" + + " Source: " + source + "\n" + + " Location: Line " + locator.getLineNumber() + " column " + locator.getColumnNumber() + "\n" + + " Error: " + ve.getMessage()); + } + return true; + } + }); + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException( + "Failed to load Alfresco Audit Schema from " + AUDIT_SCHEMA_LOCATION, e); + } + try + { + // Unmarshall with validation @SuppressWarnings("unchecked") JAXBElement auditElement = (JAXBElement) jaxbUnmarshaller.unmarshal(is); + Audit audit = auditElement.getValue(); // Done return audit; } catch (Throwable e) { - throw new AlfrescoRuntimeException( + // Dig out a SAXParseException, if there is one + Throwable saxError = ExceptionStackUtil.getCause(e, SAXParseException.class); + if (saxError != null) + { + e = saxError; + } + throw new AuditModelException( "Failed to read Audit model XML: \n" + " Source: " + source + "\n" + - " Error: " + e.getMessage(), - e); + " Error: " + e.getMessage()); } finally { @@ -268,14 +362,14 @@ public class AuditModelRegistry { // Get the data extractors and check for duplicates DataExtractors extractorsElement = audit.getDataExtractors(); + if (extractorsElement == null) + { + extractorsElement = new ObjectFactory().createDataExtractors(); + } List converterElements = extractorsElement.getDataExtractor(); for (org.alfresco.repo.audit.model._3.DataExtractor converterElement : converterElements) { String name = converterElement.getName(); - if (dataExtractorsByName.containsKey(name)) - { - throw new AuditModelException("Audit data extractor '" + name + "' has already been defined."); - } // Construct the converter final DataExtractor dataExtractor; try @@ -293,18 +387,29 @@ public class AuditModelRegistry throw new AuditModelException( "Audit data extractor '" + name + "' could not be constructed: " + converterElement.getClazz()); } + // If the name is taken, make sure that they are equal + if (dataExtractorsByName.containsKey(name)) + { + DataExtractor existing = dataExtractorsByName.get(name); + if (!existing.equals(dataExtractor)) + { + throw new AuditModelException( + "Audit data extractor '" + name + "' is incompatible with an existing instance."); + } + } + // Store dataExtractorsByName.put(name, dataExtractor); } // Get the data generators and check for duplicates DataGenerators generatorsElement = audit.getDataGenerators(); + if (generatorsElement == null) + { + generatorsElement = new ObjectFactory().createDataGenerators(); + } List generatorElements = generatorsElement.getDataGenerator(); for (org.alfresco.repo.audit.model._3.DataGenerator generatorElement : generatorElements) { String name = generatorElement.getName(); - if (dataGeneratorsByName.containsKey(name)) - { - throw new AuditModelException("Audit data generator '" + name + "' has already been defined."); - } // Construct the converter final DataGenerator dataGenerator; try @@ -322,6 +427,17 @@ public class AuditModelRegistry throw new AuditModelException( "Audit data generator '" + name + "' could not be constructed: " + generatorElement.getClazz()); } + // If the name is taken, make sure that they are equal + if (dataGeneratorsByName.containsKey(name)) + { + DataGenerator existing = dataGeneratorsByName.get(name); + if (!existing.equals(dataGenerator)) + { + throw new AuditModelException( + "Audit data generator '" + name + "' is incompatible with an existing instance."); + } + } + // Store dataGeneratorsByName.put(name, dataGenerator); } // Get the application and check for duplicates @@ -333,7 +449,8 @@ public class AuditModelRegistry { throw new AuditModelException("Audit application '" + name + "' has already been defined."); } - auditApplicationsByName.put(name, application); + AuditApplication wrapperApp = new AuditApplication(dataExtractorsByName, dataGeneratorsByName, application); + auditApplicationsByName.put(name, wrapperApp); auditModelIdsByApplicationsName.put(name, auditModelId); } // Store the model itself diff --git a/source/java/org/alfresco/repo/audit/model/_3/AuditPath.java b/source/java/org/alfresco/repo/audit/model/_3/AuditPath.java index 1fc0deb79c..6de3e1314e 100644 --- a/source/java/org/alfresco/repo/audit/model/_3/AuditPath.java +++ b/source/java/org/alfresco/repo/audit/model/_3/AuditPath.java @@ -20,7 +20,6 @@ import javax.xml.bind.annotation.XmlType; * <complexContent> * <extension base="{http://www.alfresco.org/repo/audit/model/3.2}KeyedAuditDefinition"> * <sequence> - * <element name="AuditSession" type="{http://www.alfresco.org/repo/audit/model/3.2}AuditSession" minOccurs="0"/> * <element name="RecordValue" type="{http://www.alfresco.org/repo/audit/model/3.2}RecordValue" maxOccurs="unbounded" minOccurs="0"/> * <element name="GenerateValue" type="{http://www.alfresco.org/repo/audit/model/3.2}GenerateValue" maxOccurs="unbounded" minOccurs="0"/> * <element name="AuditPath" type="{http://www.alfresco.org/repo/audit/model/3.2}AuditPath" maxOccurs="unbounded" minOccurs="0"/> @@ -34,7 +33,6 @@ import javax.xml.bind.annotation.XmlType; */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "AuditPath", propOrder = { - "auditSession", "recordValue", "generateValue", "auditPath" @@ -46,8 +44,6 @@ public class AuditPath extends KeyedAuditDefinition { - @XmlElement(name = "AuditSession") - protected AuditSession auditSession; @XmlElement(name = "RecordValue") protected List recordValue; @XmlElement(name = "GenerateValue") @@ -55,30 +51,6 @@ public class AuditPath @XmlElement(name = "AuditPath") protected List auditPath; - /** - * Gets the value of the auditSession property. - * - * @return - * possible object is - * {@link AuditSession } - * - */ - public AuditSession getAuditSession() { - return auditSession; - } - - /** - * Sets the value of the auditSession property. - * - * @param value - * allowed object is - * {@link AuditSession } - * - */ - public void setAuditSession(AuditSession value) { - this.auditSession = value; - } - /** * Gets the value of the recordValue property. * diff --git a/source/java/org/alfresco/repo/audit/model/_3/AuditSession.java b/source/java/org/alfresco/repo/audit/model/_3/AuditSession.java deleted file mode 100644 index a0e29f5f26..0000000000 --- a/source/java/org/alfresco/repo/audit/model/_3/AuditSession.java +++ /dev/null @@ -1,69 +0,0 @@ - -package org.alfresco.repo.audit.model._3; - -import java.util.ArrayList; -import java.util.List; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlType; - - -/** - *

Java class for AuditSession complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="AuditSession">
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <element name="GenerateValue" type="{http://www.alfresco.org/repo/audit/model/3.2}GenerateValue" maxOccurs="unbounded" minOccurs="0"/>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "AuditSession", propOrder = { - "generateValue" -}) -public class AuditSession { - - @XmlElement(name = "GenerateValue") - protected List generateValue; - - /** - * Gets the value of the generateValue property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the generateValue property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getGenerateValue().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link GenerateValue } - * - * - */ - public List getGenerateValue() { - if (generateValue == null) { - generateValue = new ArrayList(); - } - return this.generateValue; - } - -} diff --git a/source/java/org/alfresco/repo/audit/model/_3/GenerateValue.java b/source/java/org/alfresco/repo/audit/model/_3/GenerateValue.java index 8fcd9206d5..60caf6d316 100644 --- a/source/java/org/alfresco/repo/audit/model/_3/GenerateValue.java +++ b/source/java/org/alfresco/repo/audit/model/_3/GenerateValue.java @@ -17,6 +17,7 @@ import javax.xml.bind.annotation.XmlType; * <complexContent> * <extension base="{http://www.alfresco.org/repo/audit/model/3.2}KeyedAuditDefinition"> * <attribute name="dataGenerator" use="required" type="{http://www.alfresco.org/repo/audit/model/3.2}NameAttribute" /> + * <attribute name="scope" use="required" type="{http://www.alfresco.org/repo/audit/model/3.2}ScopeAttribute" /> * </extension> * </complexContent> * </complexType> @@ -32,6 +33,8 @@ public class GenerateValue @XmlAttribute(required = true) protected String dataGenerator; + @XmlAttribute(required = true) + protected ScopeAttribute scope; /** * Gets the value of the dataGenerator property. @@ -57,4 +60,28 @@ public class GenerateValue this.dataGenerator = value; } + /** + * Gets the value of the scope property. + * + * @return + * possible object is + * {@link ScopeAttribute } + * + */ + public ScopeAttribute getScope() { + return scope; + } + + /** + * Sets the value of the scope property. + * + * @param value + * allowed object is + * {@link ScopeAttribute } + * + */ + public void setScope(ScopeAttribute value) { + this.scope = value; + } + } diff --git a/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java b/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java index 69c9a919ef..2828f80fd4 100644 --- a/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java +++ b/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java @@ -33,6 +33,30 @@ public class ObjectFactory { public ObjectFactory() { } + /** + * Create an instance of {@link KeyedAuditDefinition } + * + */ + public KeyedAuditDefinition createKeyedAuditDefinition() { + return new KeyedAuditDefinition(); + } + + /** + * Create an instance of {@link DataExtractor } + * + */ + public DataExtractor createDataExtractor() { + return new DataExtractor(); + } + + /** + * Create an instance of {@link Audit } + * + */ + public Audit createAudit() { + return new Audit(); + } + /** * Create an instance of {@link DataExtractors } * @@ -49,6 +73,14 @@ public class ObjectFactory { return new AuditPath(); } + /** + * Create an instance of {@link DataGenerator } + * + */ + public DataGenerator createDataGenerator() { + return new DataGenerator(); + } + /** * Create an instance of {@link DataGenerators } * @@ -57,14 +89,6 @@ public class ObjectFactory { return new DataGenerators(); } - /** - * Create an instance of {@link KeyedAuditDefinition } - * - */ - public KeyedAuditDefinition createKeyedAuditDefinition() { - return new KeyedAuditDefinition(); - } - /** * Create an instance of {@link RecordValue } * @@ -73,38 +97,6 @@ public class ObjectFactory { return new RecordValue(); } - /** - * Create an instance of {@link DataGenerator } - * - */ - public DataGenerator createDataGenerator() { - return new DataGenerator(); - } - - /** - * Create an instance of {@link AuditSession } - * - */ - public AuditSession createAuditSession() { - return new AuditSession(); - } - - /** - * Create an instance of {@link GenerateValue } - * - */ - public GenerateValue createGenerateValue() { - return new GenerateValue(); - } - - /** - * Create an instance of {@link DataExtractor } - * - */ - public DataExtractor createDataExtractor() { - return new DataExtractor(); - } - /** * Create an instance of {@link Application } * @@ -114,11 +106,11 @@ public class ObjectFactory { } /** - * Create an instance of {@link Audit } + * Create an instance of {@link GenerateValue } * */ - public Audit createAudit() { - return new Audit(); + public GenerateValue createGenerateValue() { + return new GenerateValue(); } /** diff --git a/source/java/org/alfresco/repo/audit/model/_3/ScopeAttribute.java b/source/java/org/alfresco/repo/audit/model/_3/ScopeAttribute.java new file mode 100644 index 0000000000..029bcb2971 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/_3/ScopeAttribute.java @@ -0,0 +1,40 @@ + +package org.alfresco.repo.audit.model._3; + +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for ScopeAttribute. + * + *

The following schema fragment specifies the expected content contained within this class. + *

+ *

+ * <simpleType name="ScopeAttribute">
+ *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
+ *     <enumeration value="SESSION"/>
+ *     <enumeration value="AUDIT"/>
+ *     <enumeration value="ALL"/>
+ *   </restriction>
+ * </simpleType>
+ * 
+ * + */ +@XmlType(name = "ScopeAttribute") +@XmlEnum +public enum ScopeAttribute { + + SESSION, + AUDIT, + ALL; + + public String value() { + return name(); + } + + public static ScopeAttribute fromValue(String v) { + return valueOf(v); + } + +} diff --git a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-01.xml b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-01.xml new file mode 100644 index 0000000000..e20ba07aa0 --- /dev/null +++ b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-01.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-02.xml b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-02.xml new file mode 100644 index 0000000000..6e38a361ed --- /dev/null +++ b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-02.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-03.xml b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-03.xml new file mode 100644 index 0000000000..fce198634e --- /dev/null +++ b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-03.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-04.xml b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-04.xml new file mode 100644 index 0000000000..a0e47761a7 --- /dev/null +++ b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-04.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-05.xml b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-05.xml new file mode 100644 index 0000000000..752fea97b4 --- /dev/null +++ b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-05.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/test-resources/alfresco/audit/alfresco-audit-test.xml b/source/test-resources/alfresco/audit/alfresco-audit-test.xml new file mode 100644 index 0000000000..b6f3364f7f --- /dev/null +++ b/source/test-resources/alfresco/audit/alfresco-audit-test.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file