diff --git a/config/alfresco/audit/alfresco-audit-3.2.xsd b/config/alfresco/audit/alfresco-audit-3.2.xsd
index c7df8902e1..7056a05720 100644
--- a/config/alfresco/audit/alfresco-audit-3.2.xsd
+++ b/config/alfresco/audit/alfresco-audit-3.2.xsd
@@ -86,6 +86,7 @@
+
diff --git a/config/alfresco/audit/alfresco-audit-repository.xml b/config/alfresco/audit/alfresco-audit-repository.xml
index f06ea65370..de1c0f10ba 100644
--- a/config/alfresco/audit/alfresco-audit-repository.xml
+++ b/config/alfresco/audit/alfresco-audit-repository.xml
@@ -23,18 +23,18 @@
+
-
+
diff --git a/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java b/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java
index 4bf59b33c9..bda1cc6045 100644
--- a/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java
+++ b/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java
@@ -19,15 +19,16 @@
package org.alfresco.repo.audit;
import java.net.URL;
+import java.util.List;
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.model.AuditApplication;
import org.alfresco.repo.audit.model.AuditModelException;
import org.alfresco.repo.audit.model.AuditModelRegistryImpl;
+import org.alfresco.repo.audit.model.AuditApplication.DataExtractorDefinition;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.PathMapper;
import org.apache.commons.logging.Log;
@@ -188,17 +189,9 @@ public class AuditBootstrapTest extends TestCase
AuditApplication app = auditModelRegistry.getAuditApplicationByName(APPLICATION_TEST);
assertNotNull(app);
- Map extractors = app.getDataExtractors("/blah");
- assertNotNull("Should never get a null map", extractors);
- assertTrue("Expected no extractors", extractors.isEmpty());
-
- extractors = app.getDataExtractors("/test/1.1/2.1/3.1/4.1");
- assertEquals(1, extractors.size());
- assertTrue(extractors.containsKey("/test/1.1/2.1/3.1/4.1/value.1"));
-
- extractors = app.getDataExtractors("/test/1.1/2.1/3.1");
- assertEquals(1, extractors.size());
- assertTrue(extractors.containsKey("/test/1.1/2.1/3.1/value.1"));
+ List extractors = app.getDataExtractors();
+ assertNotNull("Should never get a null list", extractors);
+ assertEquals("Expected 13 extractors", 13, extractors.size());
}
public void testAuditApplication_GetDataGenerators()
diff --git a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java
index af2705058d..2034d2193c 100644
--- a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java
+++ b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java
@@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -32,6 +33,7 @@ import org.alfresco.repo.audit.generator.DataGenerator;
import org.alfresco.repo.audit.model.AuditApplication;
import org.alfresco.repo.audit.model.AuditModelRegistry;
import org.alfresco.repo.audit.model.AuditModelRegistryImpl;
+import org.alfresco.repo.audit.model.AuditApplication.DataExtractorDefinition;
import org.alfresco.repo.domain.audit.AuditDAO;
import org.alfresco.repo.domain.propval.PropertyValueDAO;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -561,21 +563,7 @@ public class AuditComponentImpl implements AuditComponent
throw new AuditException("No persisted instance exists for audit application: " + application);
}
- // Eliminate any paths that have been disabled
- Iterator pathedValuesKeyIterator = values.keySet().iterator();
- while(pathedValuesKeyIterator.hasNext())
- {
- String pathedValueKey = pathedValuesKeyIterator.next();
- for (String disabledPath : disabledPaths)
- {
- if (pathedValueKey.startsWith(disabledPath))
- {
- // The pathed value is excluded
- pathedValuesKeyIterator.remove();
- }
- }
- }
- // Check if there is anything left
+ // Check if there is anything to audit
if (values.size() == 0)
{
if (logger.isDebugEnabled())
@@ -587,9 +575,25 @@ public class AuditComponentImpl implements AuditComponent
}
return Collections.emptyMap();
}
+
+ Set generatorKeys = values.keySet();
+ // Eliminate any paths that have been disabled
+ Iterator generatorKeysIterator = generatorKeys.iterator();
+ while(generatorKeysIterator.hasNext())
+ {
+ String generatorKey = generatorKeysIterator.next();
+ for (String disabledPath : disabledPaths)
+ {
+ if (generatorKey.startsWith(disabledPath))
+ {
+ // The pathed value is excluded
+ generatorKeysIterator.remove();
+ }
+ }
+ }
// Generate data
- Map generators = application.getDataGenerators(values.keySet());
+ Map generators = application.getDataGenerators(generatorKeys);
Map auditData = generateData(generators);
// Now extract values
@@ -641,40 +645,45 @@ public class AuditComponentImpl implements AuditComponent
AuditApplication application,
Map values)
{
- Map newData = new HashMap(values.size() + 5);
- for (Map.Entry entry : values.entrySet())
+ Map newData = new HashMap(values.size());
+
+ List extractors = application.getDataExtractors();
+ for (DataExtractorDefinition extractorDef : extractors)
{
- String path = entry.getKey();
- Serializable value = entry.getValue();
- // Get the applicable extractor
- Map extractors = application.getDataExtractors(path);
- for (Map.Entry extractorElement : extractors.entrySet())
+ DataExtractor extractor = extractorDef.getDataExtractor();
+ String sourcePath = extractorDef.getDataSource();
+ String targetPath = extractorDef.getDataTarget();
+
+ // We observe the key, not the actual value
+ if (!values.containsKey(sourcePath))
{
- String extractorPath = extractorElement.getKey();
- DataExtractor extractor = extractorElement.getValue();
- // Check if the extraction is supported
- if (!extractor.isSupported(value))
- {
- continue;
- }
- // Use the extractor to pull the value out
- final Serializable data;
- try
- {
- data = extractor.extractData(value);
- }
- catch (Throwable e)
- {
- throw new AlfrescoRuntimeException(
- "Failed to extract audit data: \n" +
- " Path: " + path + "\n" +
- " Raw value: " + value + "\n" +
- " Extractor: " + extractor,
- e);
- }
- // Add it to the map
- newData.put(extractorPath, data);
+ continue; // There is no data to extract
}
+
+ Serializable value = values.get(sourcePath);
+
+ // Check if the extraction is supported
+ if (!extractor.isSupported(value))
+ {
+ continue;
+ }
+ // Use the extractor to pull the value out
+ final Serializable data;
+ try
+ {
+ data = extractor.extractData(value);
+ }
+ catch (Throwable e)
+ {
+ throw new AlfrescoRuntimeException(
+ "Failed to extract audit data: \n" +
+ " Path: " + sourcePath + "\n" +
+ " Raw value: " + value + "\n" +
+ " Extractor: " + extractor,
+ e);
+ }
+ // Add it to the map
+ newData.put(targetPath, data);
}
// Done
if (logger.isDebugEnabled())
@@ -682,7 +691,7 @@ public class AuditComponentImpl implements AuditComponent
logger.debug("Extracted audit data: \n" +
" Application: " + application + "\n" +
" Raw values: " + values + "\n" +
- " Extracted: " + newData);
+ " Extracted: " + newData);
}
return newData;
}
diff --git a/source/java/org/alfresco/repo/audit/AuditComponentTest.java b/source/java/org/alfresco/repo/audit/AuditComponentTest.java
index 223f80edfd..18239918f7 100644
--- a/source/java/org/alfresco/repo/audit/AuditComponentTest.java
+++ b/source/java/org/alfresco/repo/audit/AuditComponentTest.java
@@ -253,7 +253,7 @@ public class AuditComponentTest extends TestCase
Serializable valueA = new Date();
Serializable valueB = "BBB-value-here";
Serializable valueC = new Float(16.0F);
- // Get a noderef
+
final Map parameters = new HashMap(13);
parameters.put("A", valueA);
parameters.put("B", valueB);
@@ -292,6 +292,45 @@ public class AuditComponentTest extends TestCase
auditAction01("action-01-mapped");
}
+ /**
+ * Test auditing of something resembling real-world data
+ */
+ private void auditAction02(String actionName) throws Exception
+ {
+ Serializable valueA = new Date();
+ Serializable valueB = "BBB-value-here";
+ Serializable valueC = new Float(16.0F);
+
+ final Map parameters = new HashMap(13);
+ parameters.put("A", valueA);
+ parameters.put("B", valueB);
+ parameters.put("C", valueC);
+ // lowercase versions are not in the config
+ parameters.put("a", valueA);
+ parameters.put("b", valueB);
+ parameters.put("c", valueC);
+
+ Map result = auditTestAction(actionName, nodeRef, parameters);
+
+ Map expected = new HashMap();
+ expected.put("/actions-test/actions/user", AuthenticationUtil.getFullyAuthenticatedUser());
+ expected.put("/actions-test/actions/context-node/noderef", nodeRef);
+ expected.put("/actions-test/actions/action-02/valueA", valueA);
+ expected.put("/actions-test/actions/action-02/valueB", valueB);
+ expected.put("/actions-test/actions/action-02/valueC", valueC);
+
+ // Check
+ checkAuditMaps(result, expected);
+ }
+
+ /**
+ * Test auditing using alternative data sources
+ */
+ public void testAudit_Action02Sourced() throws Exception
+ {
+ auditAction02("action-02-sourced");
+ }
+
public void testQuery_Action01() throws Exception
{
final Long beforeTime = new Long(System.currentTimeMillis());
diff --git a/source/java/org/alfresco/repo/audit/model/AuditApplication.java b/source/java/org/alfresco/repo/audit/model/AuditApplication.java
index 3c24795622..d51a539f1f 100644
--- a/source/java/org/alfresco/repo/audit/model/AuditApplication.java
+++ b/source/java/org/alfresco/repo/audit/model/AuditApplication.java
@@ -18,9 +18,11 @@
*/
package org.alfresco.repo.audit.model;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -60,7 +62,7 @@ public class AuditApplication
private final Long disabledPathsId;
/** Derived expaned map for fast lookup */
- private Map> dataExtractors = new HashMap>(11);
+ private List dataExtractors = new ArrayList();
/** Derived expaned map for fast lookup */
private Map> dataGenerators = new HashMap>(11);
@@ -281,31 +283,59 @@ public class AuditApplication
}
/**
- * Get all data extractors applicable to a given path and scope.
+ * Utility class carrying information around a {@link DataExtractor}.
*
- * @param path the audit path
- * @return Returns all data extractors mapped to their key-path
+ * @author Derek Hulley
+ * @since 3.4
*/
- public Map getDataExtractors(String path)
+ public static class DataExtractorDefinition
{
- Map extractors = dataExtractors.get(path);
- if (extractors == null)
+ private final String dataSource;
+ private final String dataTarget;
+ private final DataExtractor dataExtractor;
+
+ /**
+ * @param dataSource the path to get data from
+ * @param dataTarget the path to write data to
+ * @param dataExtractor the implementation to use
+ */
+ public DataExtractorDefinition(String dataSource, String dataTarget, DataExtractor dataExtractor)
{
- // Don't give back a null
- extractors = Collections.emptyMap();
+ this.dataSource = dataSource;
+ this.dataTarget = dataTarget;
+ this.dataExtractor = dataExtractor;
}
- else
+
+ public String getDataSource()
{
- // we don't want to give back a modifiable map
- extractors = Collections.unmodifiableMap(extractors);
+ return dataSource;
}
+
+ public String getDataTarget()
+ {
+ return dataTarget;
+ }
+
+ public DataExtractor getDataExtractor()
+ {
+ return dataExtractor;
+ }
+ }
+
+ /**
+ * Get all data extractors applicable to this application.
+ *
+ * @return Returns all data extractors contained in the application
+ */
+ public List getDataExtractors()
+ {
+ List extractors = Collections.unmodifiableList(dataExtractors);
// Done
if (logger.isDebugEnabled())
{
logger.debug(
"Looked up data extractors: \n" +
- " Path: " + path + "\n" +
" Found: " + extractors);
}
return extractors;
@@ -361,7 +391,6 @@ public class AuditApplication
auditPath,
null,
new HashSet(37),
- new HashMap(13),
new HashMap(13));
}
/**
@@ -371,11 +400,9 @@ public class AuditApplication
AuditPath auditPath,
String currentPath,
Set existingPaths,
- Map upperExtractorsByPath,
Map upperGeneratorsByPath)
{
// Clone the upper maps to prevent pollution
- upperExtractorsByPath = new HashMap(upperExtractorsByPath);
upperGeneratorsByPath = new HashMap(upperGeneratorsByPath);
// Append the current audit path to the current path
@@ -409,16 +436,16 @@ public class AuditApplication
{
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);
+ // The extractor may pull data from somewhere else
+ String sourcePath = element.getDataSource();
+ if (sourcePath == null)
+ {
+ sourcePath = currentPath;
+ }
+ // Store the extractor definition
+ DataExtractorDefinition extractorDef = new DataExtractorDefinition(sourcePath, extractorPath, extractor);
+ dataExtractors.add(extractorDef);
}
- // All the extractors apply to the current path
- dataExtractors.put(currentPath, upperExtractorsByPath);
- // Data extractors only apply directly to data in which they appear.
- // TODO: Examine this assumption. If it is not true, i.e. data extractors apply to
- // data anywhere down the hierarchy, then the followin line of code should be
- // removed and the use-cases tested appropriately.
- upperExtractorsByPath = new HashMap();
// Get the data generators declared for this key
for (GenerateValue element : auditPath.getGenerateValue())
@@ -445,7 +472,7 @@ public class AuditApplication
// Find all sub audit paths and recurse
for (AuditPath element : auditPath.getAuditPath())
{
- buildAuditPaths(element, currentPath, existingPaths, upperExtractorsByPath, upperGeneratorsByPath);
+ buildAuditPaths(element, currentPath, existingPaths, upperGeneratorsByPath);
}
}
diff --git a/source/java/org/alfresco/repo/audit/model/_3/KeyedAuditDefinition.java b/source/java/org/alfresco/repo/audit/model/_3/KeyedAuditDefinition.java
index 1cbdf5c9d3..f56bde15b0 100644
--- a/source/java/org/alfresco/repo/audit/model/_3/KeyedAuditDefinition.java
+++ b/source/java/org/alfresco/repo/audit/model/_3/KeyedAuditDefinition.java
@@ -45,9 +45,9 @@ import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "KeyedAuditDefinition")
@XmlSeeAlso({
- GenerateValue.class,
+ RecordValue.class,
AuditPath.class,
- RecordValue.class
+ GenerateValue.class
})
public class KeyedAuditDefinition {
diff --git a/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java b/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java
index aa542d2452..15a6cfd153 100644
--- a/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java
+++ b/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java
@@ -59,11 +59,19 @@ public class ObjectFactory {
}
/**
- * Create an instance of {@link DataGenerator }
+ * Create an instance of {@link RecordValue }
*
*/
- public DataGenerator createDataGenerator() {
- return new DataGenerator();
+ public RecordValue createRecordValue() {
+ return new RecordValue();
+ }
+
+ /**
+ * Create an instance of {@link PathMap }
+ *
+ */
+ public PathMap createPathMap() {
+ return new PathMap();
}
/**
@@ -74,14 +82,6 @@ public class ObjectFactory {
return new AuditPath();
}
- /**
- * Create an instance of {@link KeyedAuditDefinition }
- *
- */
- public KeyedAuditDefinition createKeyedAuditDefinition() {
- return new KeyedAuditDefinition();
- }
-
/**
* Create an instance of {@link GenerateValue }
*
@@ -90,30 +90,6 @@ public class ObjectFactory {
return new GenerateValue();
}
- /**
- * Create an instance of {@link DataGenerators }
- *
- */
- public DataGenerators createDataGenerators() {
- return new DataGenerators();
- }
-
- /**
- * Create an instance of {@link DataExtractors }
- *
- */
- public DataExtractors createDataExtractors() {
- return new DataExtractors();
- }
-
- /**
- * Create an instance of {@link RecordValue }
- *
- */
- public RecordValue createRecordValue() {
- return new RecordValue();
- }
-
/**
* Create an instance of {@link Application }
*
@@ -123,11 +99,27 @@ public class ObjectFactory {
}
/**
- * Create an instance of {@link DataExtractor }
+ * Create an instance of {@link KeyedAuditDefinition }
*
*/
- public DataExtractor createDataExtractor() {
- return new DataExtractor();
+ public KeyedAuditDefinition createKeyedAuditDefinition() {
+ return new KeyedAuditDefinition();
+ }
+
+ /**
+ * Create an instance of {@link DataExtractors }
+ *
+ */
+ public DataExtractors createDataExtractors() {
+ return new DataExtractors();
+ }
+
+ /**
+ * Create an instance of {@link DataGenerator }
+ *
+ */
+ public DataGenerator createDataGenerator() {
+ return new DataGenerator();
}
/**
@@ -139,11 +131,19 @@ public class ObjectFactory {
}
/**
- * Create an instance of {@link PathMap }
+ * Create an instance of {@link DataExtractor }
*
*/
- public PathMap createPathMap() {
- return new PathMap();
+ public DataExtractor createDataExtractor() {
+ return new DataExtractor();
+ }
+
+ /**
+ * Create an instance of {@link DataGenerators }
+ *
+ */
+ public DataGenerators createDataGenerators() {
+ return new DataGenerators();
}
/**
diff --git a/source/java/org/alfresco/repo/audit/model/_3/RecordValue.java b/source/java/org/alfresco/repo/audit/model/_3/RecordValue.java
index cc9c2036c9..309c60faae 100644
--- a/source/java/org/alfresco/repo/audit/model/_3/RecordValue.java
+++ b/source/java/org/alfresco/repo/audit/model/_3/RecordValue.java
@@ -34,6 +34,7 @@ import javax.xml.bind.annotation.XmlType;
* <complexContent>
* <extension base="{http://www.alfresco.org/repo/audit/model/3.2}KeyedAuditDefinition">
* <attribute name="dataExtractor" use="required" type="{http://www.alfresco.org/repo/audit/model/3.2}NameAttribute" />
+ * <attribute name="dataSource" type="{http://www.alfresco.org/repo/audit/model/3.2}PathAttribute" />
* </extension>
* </complexContent>
* </complexType>
@@ -49,6 +50,8 @@ public class RecordValue
@XmlAttribute(required = true)
protected String dataExtractor;
+ @XmlAttribute
+ protected String dataSource;
/**
* Gets the value of the dataExtractor property.
@@ -74,4 +77,28 @@ public class RecordValue
this.dataExtractor = value;
}
+ /**
+ * Gets the value of the dataSource property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getDataSource() {
+ return dataSource;
+ }
+
+ /**
+ * Sets the value of the dataSource property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setDataSource(String value) {
+ this.dataSource = value;
+ }
+
}
diff --git a/source/test-resources/alfresco/testaudit/alfresco-audit-test.xml b/source/test-resources/alfresco/testaudit/alfresco-audit-test.xml
index c6da38addf..1c1dd1de61 100644
--- a/source/test-resources/alfresco/testaudit/alfresco-audit-test.xml
+++ b/source/test-resources/alfresco/testaudit/alfresco-audit-test.xml
@@ -24,6 +24,7 @@
+
@@ -101,6 +102,11 @@
+
+
+
+
+