Audit validation using XSD and related tests

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@15875 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2009-08-24 20:10:49 +00:00
parent b8c93f89f3
commit 598ecfb27b
27 changed files with 1112 additions and 224 deletions

View File

@@ -79,6 +79,7 @@
<!-- -->
<bean id="auditModel.registry" class="org.alfresco.repo.audit.model.AuditModelRegistry">
<property name="transactionService" ref="transactionService"/>
<property name="auditDAO" ref="auditDAO"/>
</bean>

View File

@@ -57,7 +57,6 @@
<xs:complexContent>
<xs:extension base="a:KeyedAuditDefinition">
<xs:sequence>
<xs:element name="AuditSession" type="a:AuditSession" minOccurs="0" maxOccurs="1"/>
<xs:element name="RecordValue" type="a:RecordValue" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="GenerateValue" type="a:GenerateValue" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="AuditPath" type="a:AuditPath" minOccurs="0" maxOccurs="unbounded"/>
@@ -66,12 +65,6 @@
</xs:complexContent>
</xs:complexType>
<xs:complexType name="AuditSession">
<xs:sequence>
<xs:element name="GenerateValue" type="a:GenerateValue" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="RecordValue">
<xs:complexContent>
<xs:extension base="a:KeyedAuditDefinition">
@@ -84,6 +77,7 @@
<xs:complexContent>
<xs:extension base="a:KeyedAuditDefinition">
<xs:attribute name="dataGenerator" type="a:NameAttribute" use="required" />
<xs:attribute name="scope" type="a:ScopeAttribute" use="required" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
@@ -108,7 +102,7 @@
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="128"/>
<xs:pattern value="([a-z]|\-)*"/>
<xs:pattern value="([a-z]|[0-9]|\-|\.)*"/>
</xs:restriction>
</xs:simpleType>
@@ -118,4 +112,12 @@
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ScopeAttribute">
<xs:restriction base="xs:string">
<xs:enumeration value="SESSION"/>
<xs:enumeration value="AUDIT"/>
<xs:enumeration value="ALL"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>

View File

@@ -23,12 +23,10 @@
</DataGenerators>
<Application name="Alfresco Repository" key="repository">
<AuditSession>
<GenerateValue key="user" dataGenerator="authenticatedUser"/>
<GenerateValue key="time" dataGenerator="systemTime"/>
</AuditSession>
<GenerateValue key="user" dataGenerator="authenticatedUser" scope="SESSION"/>
<GenerateValue key="time" dataGenerator="systemTime" scope="SESSION"/>
<AuditPath key="services">
<GenerateValue key="txn" dataGenerator="transactionId"/>
<GenerateValue key="txn" dataGenerator="transactionId" scope="AUDIT"/>
<AuditPath key="nodeservice">
<AuditPath key="createstore">
<AuditPath key="protocol">
@@ -39,10 +37,12 @@
</AuditPath>
<AuditPath key="return">
<RecordValue key="value" dataExtractor="simpleValue"/>
<!--
<RecordValue key="root-node" dataExtractor="storeRootNode"/>
</AuditPath>
<AuditPath key="error">
<RecordValue key="value" dataExtractor="stackTrace"/>
-->
</AuditPath>
</AuditPath>
</AuditPath>

View File

@@ -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<String, DataExtractor> 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<String, DataGenerator> 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"));
}
}

View File

@@ -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 <b>.</b> (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<String, Object> values);
public void audit(AuditSession session, Map<String, Object> values);
}

View File

@@ -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<Long, AuditSession> 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<String, Object> values)
public void audit(AuditSession session, Map<String, Object> values)
{
if (sessionId == NO_AUDIT_SESSION)
if (session == NO_AUDIT_SESSION)
{
// For some reason, the session was not to be used
return;

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -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 <tt>true</tt> always

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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.
*

View File

@@ -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

View File

@@ -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 (<tt>null</tt> if not in a transction)

View File

@@ -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<String, DataExtractor> dataExtractorsByName;
private final Map<String, DataGenerator> dataGeneratorsByName;
@SuppressWarnings("unused")
private final Application application;
/** Derived expaned map for fast lookup */
private Map<String, Map<String, DataExtractor>> dataExtractors = new HashMap<String, Map<String, DataExtractor>>(11);
/** Derived expaned map for fast lookup */
private Map<String, Map<String, DataGenerator>> dataGenerators = new HashMap<String, Map<String, DataGenerator>>(11);
/** Derived expaned map for fast lookup */
private Map<String, DataGeneratorScope> dataGeneratorScopes = new HashMap<String, DataGeneratorScope>(11);
/**
* @param application the application that will be wrapped
* @param dataExtractorsByName data extractors to use
* @param dataGeneratorsByName data generators to use
*/
/* package */ AuditApplication(
Map<String, DataExtractor> dataExtractorsByName,
Map<String, DataGenerator> 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 <b>/app-key/x/y/z</b>
* @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<String, DataExtractor> getDataExtractors(String path)
{
Map<String, DataExtractor> 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<String, DataGenerator> getDataGenerators(String path, DataGeneratorScope scope)
{
Map<String, DataGenerator> generators = dataGenerators.get(path);
if (generators == null)
{
// Don't give back a null
generators = new HashMap<String, DataGenerator>(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<String, DataGenerator>(generators);
}
if (scope != DataGeneratorScope.ALL)
{
// Go through them and eliminate the ones in the wrong scope
Iterator<Map.Entry<String, DataGenerator>> iterator = generators.entrySet().iterator();
while (iterator.hasNext())
{
Map.Entry<String, DataGenerator> 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<String>(37),
new HashMap<String, DataExtractor>(13),
new HashMap<String, DataGenerator>(13));
}
/**
* Recursive method to build generator and extractor mappings
*/
private void buildAuditPaths(
AuditPath auditPath,
String currentPath,
Set<String> existingPaths,
Map<String, DataExtractor> upperExtractorsByPath,
Map<String, DataGenerator> upperGeneratorsByPath)
{
// Clone the upper maps to prevent pollution
upperExtractorsByPath = new HashMap<String, DataExtractor>(upperExtractorsByPath);
upperGeneratorsByPath = new HashMap<String, DataGenerator>(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);
}
}

View File

@@ -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<String, Application> auditApplicationsByName;
private final Map<String, AuditApplication> auditApplicationsByName;
/**
* Used to lookup a reference to the persisted config binary for an application
*/
@@ -92,10 +108,18 @@ public class AuditModelRegistry
auditModels = new ArrayList<Audit>(7);
dataExtractorsByName = new HashMap<String, DataExtractor>(13);
dataGeneratorsByName = new HashMap<String, DataGenerator>(13);
auditApplicationsByName = new HashMap<String, Application>(7);
auditApplicationsByName = new HashMap<String, AuditApplication>(7);
auditModelIdsByApplicationsName = new HashMap<String, Long>(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.
* <p/>
* Note, the models are loaded in a new transaction.
*/
public void loadAuditModels()
{
writeLock.lock();
try
RetryingTransactionCallback<Void> loadModelsCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
// Load models from the URLs
for (URL auditModelUrl : auditModelUrls)
Set<URL> auditModelUrlsInner = new HashSet<URL>(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
{
transactionService.getRetryingTransactionHelper().doInTransaction(loadModelsCallback);
}
finally
{
@@ -197,7 +261,7 @@ public class AuditModelRegistry
* @param application the name of the audited application
* @return the java model (<tt>null</tt> 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<Audit> auditElement = (JAXBElement<Audit>) 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<org.alfresco.repo.audit.model._3.DataExtractor> 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<org.alfresco.repo.audit.model._3.DataGenerator> 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

View File

@@ -20,7 +20,6 @@ import javax.xml.bind.annotation.XmlType;
* &lt;complexContent>
* &lt;extension base="{http://www.alfresco.org/repo/audit/model/3.2}KeyedAuditDefinition">
* &lt;sequence>
* &lt;element name="AuditSession" type="{http://www.alfresco.org/repo/audit/model/3.2}AuditSession" minOccurs="0"/>
* &lt;element name="RecordValue" type="{http://www.alfresco.org/repo/audit/model/3.2}RecordValue" maxOccurs="unbounded" minOccurs="0"/>
* &lt;element name="GenerateValue" type="{http://www.alfresco.org/repo/audit/model/3.2}GenerateValue" maxOccurs="unbounded" minOccurs="0"/>
* &lt;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> recordValue;
@XmlElement(name = "GenerateValue")
@@ -55,30 +51,6 @@ public class AuditPath
@XmlElement(name = "AuditPath")
protected List<AuditPath> 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.
*

View File

@@ -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;
/**
* <p>Java class for AuditSession complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* &lt;complexType name="AuditSession">
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element name="GenerateValue" type="{http://www.alfresco.org/repo/audit/model/3.2}GenerateValue" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "AuditSession", propOrder = {
"generateValue"
})
public class AuditSession {
@XmlElement(name = "GenerateValue")
protected List<GenerateValue> generateValue;
/**
* Gets the value of the generateValue property.
*
* <p>
* 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 <CODE>set</CODE> method for the generateValue property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getGenerateValue().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link GenerateValue }
*
*
*/
public List<GenerateValue> getGenerateValue() {
if (generateValue == null) {
generateValue = new ArrayList<GenerateValue>();
}
return this.generateValue;
}
}

View File

@@ -17,6 +17,7 @@ import javax.xml.bind.annotation.XmlType;
* &lt;complexContent>
* &lt;extension base="{http://www.alfresco.org/repo/audit/model/3.2}KeyedAuditDefinition">
* &lt;attribute name="dataGenerator" use="required" type="{http://www.alfresco.org/repo/audit/model/3.2}NameAttribute" />
* &lt;attribute name="scope" use="required" type="{http://www.alfresco.org/repo/audit/model/3.2}ScopeAttribute" />
* &lt;/extension>
* &lt;/complexContent>
* &lt;/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;
}
}

View File

@@ -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();
}
/**

View File

@@ -0,0 +1,40 @@
package org.alfresco.repo.audit.model._3;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for ScopeAttribute.
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
* <pre>
* &lt;simpleType name="ScopeAttribute">
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string">
* &lt;enumeration value="SESSION"/>
* &lt;enumeration value="AUDIT"/>
* &lt;enumeration value="ALL"/>
* &lt;/restriction>
* &lt;/simpleType>
* </pre>
*
*/
@XmlType(name = "ScopeAttribute")
@XmlEnum
public enum ScopeAttribute {
SESSION,
AUDIT,
ALL;
public String value() {
return name();
}
public static ScopeAttribute fromValue(String v) {
return valueOf(v);
}
}

View File

@@ -0,0 +1,19 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Default Audit Configuration -->
<Audit
xmlns="http://www.alfresco.org/repo/audit/model/3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.alfresco.org/repo/audit/model/3.2 alfresco-audit-3.2.xsd"
>
<Application name="Alfresco Test Bad 01" key="test-bad-01">
<AuditPath key="1.1">
<AuditPath key="2.1">
<RecordValue key="value.1" dataExtractor="blah"/>
</AuditPath>
</AuditPath>
</Application>
</Audit>

View File

@@ -0,0 +1,19 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Default Audit Configuration -->
<Audit
xmlns="http://www.alfresco.org/repo/audit/model/3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.alfresco.org/repo/audit/model/3.2 alfresco-audit-3.2.xsd"
>
<Application name="Alfresco Test Bad 02" key="test-bad-02">
<AuditPath key="1.1">
<AuditPath key="2.1">
<GenerateValue key="value.1" dataGenerator="blah" scope="AUDIT"/>
</AuditPath>
</AuditPath>
</Application>
</Audit>

View File

@@ -0,0 +1,22 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Default Audit Configuration -->
<Audit
xmlns="http://www.alfresco.org/repo/audit/model/3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.alfresco.org/repo/audit/model/3.2 alfresco-audit-3.2.xsd"
>
<Application name="Alfresco Test Bad 03" key="test-bad-03">
<AuditPath key="1.1">
<AuditPath key="2.1">
<RecordValue key="value.1" dataExtractor="simpleValue"/>
</AuditPath>
<AuditPath key="2.1">
<RecordValue key="value.1" dataExtractor="simpleValue"/>
</AuditPath>
</AuditPath>
</Application>
</Audit>

View File

@@ -0,0 +1,19 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Default Audit Configuration -->
<Audit
xmlns="http://www.alfresco.org/repo/audit/model/3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.alfresco.org/repo/audit/model/3.2 alfresco-audit-3.2.xsd"
>
<Application name="Alfresco Test Bad 04" key="test-bad-04">
<AuditPath key="1.1">
<AuditPath key="2.1">
<RecordValue key="value.UPPER" dataExtractor="simpleValue"/>
</AuditPath>
</AuditPath>
</Application>
</Audit>

View File

@@ -0,0 +1,19 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Default Audit Configuration -->
<Audit
xmlns="http://www.alfresco.org/repo/audit/model/3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.alfresco.org/repo/audit/model/3.2 alfresco-audit-3.2.xsd"
>
<Application name="Alfresco Test Bad 05" key="test-bad-05">
<AuditPath key="1.1">
<AuditPath key="2.1">
<GenerateValue key="time" dataGenerator="systemTime" scope="SESSIOn"/>
</AuditPath>
</AuditPath>
</Application>
</Audit>

View File

@@ -0,0 +1,66 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Default Audit Configuration -->
<Audit
xmlns="http://www.alfresco.org/repo/audit/model/3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.alfresco.org/repo/audit/model/3.2 alfresco-audit-3.2.xsd"
>
<!-- Includes duplicate definitions of the extractors and generators -->
<DataExtractors>
<DataExtractor name="simpleValue" class="org.alfresco.repo.audit.extractor.SimpleValueDataExtractor"/>
</DataExtractors>
<DataGenerators>
<DataGenerator name="systemTime" class="org.alfresco.repo.audit.generator.SystemTimeDataGenerator"/>
</DataGenerators>
<Application name="Alfresco Test" key="test">
<GenerateValue key="time" dataGenerator="systemTime" scope="SESSION"/>
<AuditPath key="1.1">
<AuditPath key="2.1">
<AuditPath key="3.1">
<RecordValue key="value.1" dataExtractor="simpleValue"/>
<AuditPath key="4.1">
<RecordValue key="value.1" dataExtractor="simpleValue"/>
</AuditPath>
<AuditPath key="4.2">
<RecordValue key="value.2" dataExtractor="simpleValue"/>
</AuditPath>
</AuditPath>
<AuditPath key="3.2">
<AuditPath key="4.1">
<RecordValue key="value.1" dataExtractor="simpleValue"/>
<GenerateValue key="time" dataGenerator="systemTime" scope="AUDIT"/>
</AuditPath>
<AuditPath key="4.2">
<RecordValue key="value.1" dataExtractor="simpleValue"/>
</AuditPath>
</AuditPath>
</AuditPath>
<AuditPath key="2.2">
<AuditPath key="3.1">
<AuditPath key="4.1">
<RecordValue key="value.1" dataExtractor="simpleValue"/>
</AuditPath>
<AuditPath key="4.2">
<RecordValue key="value.2" dataExtractor="simpleValue"/>
</AuditPath>
</AuditPath>
<AuditPath key="3.2">
<AuditPath key="4.1">
<RecordValue key="value.1" dataExtractor="simpleValue"/>
<GenerateValue key="time" dataGenerator="systemTime" scope="ALL"/>
</AuditPath>
<AuditPath key="4.2">
<RecordValue key="value.1" dataExtractor="simpleValue"/>
</AuditPath>
</AuditPath>
</AuditPath>
</AuditPath>
</Application>
</Audit>