mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
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:
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
395
source/java/org/alfresco/repo/audit/model/AuditApplication.java
Normal file
395
source/java/org/alfresco/repo/audit/model/AuditApplication.java
Normal 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);
|
||||
}
|
||||
}
|
@@ -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()
|
||||
{
|
||||
RetryingTransactionCallback<Void> loadModelsCallback = new RetryingTransactionCallback<Void>()
|
||||
{
|
||||
public Void execute() throws Throwable
|
||||
{
|
||||
// Load models from the URLs
|
||||
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
|
||||
{
|
||||
// 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 (<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
|
||||
|
@@ -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> 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.
|
||||
*
|
||||
|
@@ -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>
|
||||
* <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>
|
||||
* </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;
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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>
|
||||
* <simpleType name="ScopeAttribute">
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}string">
|
||||
* <enumeration value="SESSION"/>
|
||||
* <enumeration value="AUDIT"/>
|
||||
* <enumeration value="ALL"/>
|
||||
* </restriction>
|
||||
* </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);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user