diff --git a/config/alfresco/audit/alfresco-audit-3.2.xsd b/config/alfresco/audit/alfresco-audit-3.2.xsd index 60f16aa225..810d82f275 100644 --- a/config/alfresco/audit/alfresco-audit-3.2.xsd +++ b/config/alfresco/audit/alfresco-audit-3.2.xsd @@ -79,7 +79,6 @@ - @@ -114,12 +113,4 @@ - - - - - - - - \ No newline at end of file diff --git a/config/alfresco/audit/alfresco-audit-repository.xml b/config/alfresco/audit/alfresco-audit-repository.xml index 955a7e7c3b..a3c2e115cb 100644 --- a/config/alfresco/audit/alfresco-audit-repository.xml +++ b/config/alfresco/audit/alfresco-audit-repository.xml @@ -22,7 +22,7 @@ - + diff --git a/config/alfresco/dbscripts/create/3.2/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.2-AuditTables.sql b/config/alfresco/dbscripts/create/3.2/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.2-AuditTables.sql index ecdb9c3af1..e1b7cbf6fd 100644 --- a/config/alfresco/dbscripts/create/3.2/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.2-AuditTables.sql +++ b/config/alfresco/dbscripts/create/3.2/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-3.2-AuditTables.sql @@ -17,24 +17,24 @@ CREATE TABLE alf_audit_model PRIMARY KEY (id) ) ENGINE=InnoDB; -CREATE TABLE alf_audit_session +CREATE TABLE alf_audit_app ( id BIGINT NOT NULL AUTO_INCREMENT, audit_model_id BIGINT NOT NULL, app_name_id BIGINT NOT NULL, - CONSTRAINT fk_alf_audit_sess_model FOREIGN KEY (audit_model_id) REFERENCES alf_audit_model (id), - CONSTRAINT fk_alf_audit_sess_app FOREIGN KEY (app_name_id) REFERENCES alf_prop_value (id), + CONSTRAINT fk_alf_audit_app_model FOREIGN KEY (audit_model_id) REFERENCES alf_audit_model (id) ON DELETE CASCADE, + CONSTRAINT fk_alf_audit_app_app FOREIGN KEY (app_name_id) REFERENCES alf_prop_value (id), PRIMARY KEY (id) ) ENGINE=InnoDB; CREATE TABLE alf_audit_entry ( id BIGINT NOT NULL AUTO_INCREMENT, - audit_session_id BIGINT NOT NULL, + audit_app_id BIGINT NOT NULL, audit_time BIGINT NOT NULL, audit_user_id BIGINT NULL, audit_values_id BIGINT NULL, - CONSTRAINT fk_alf_audit_ent_sess FOREIGN KEY (audit_session_id) REFERENCES alf_audit_session (id), + CONSTRAINT fk_alf_audit_ent_app FOREIGN KEY (audit_app_id) REFERENCES alf_audit_app (id) ON DELETE CASCADE, INDEX idx_alf_audit_ent_time (audit_time), CONSTRAINT fk_alf_audit_ent_user FOREIGN KEY (audit_user_id) REFERENCES alf_prop_value (id), CONSTRAINT fk_alf_audit_ent_prop FOREIGN KEY (audit_values_id) REFERENCES alf_prop_value (id), diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/audit-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/audit-common-SqlMap.xml index ddfac2d6f4..9717e2184d 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/audit-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/audit-common-SqlMap.xml @@ -11,7 +11,7 @@ - + @@ -23,14 +23,14 @@ - + - + @@ -53,14 +53,14 @@ values (#contentDataId#, #contentCrc#) - - insert into alf_audit_session (audit_model_id, app_name_id) + + insert into alf_audit_app (audit_model_id, app_name_id) values (#auditModelId#, #applicationNameId#) - insert into alf_audit_entry (audit_session_id, audit_user_id, audit_time, audit_values_id) - values (#auditSessionId#, #auditUserId#, #auditTime#, #auditValuesId#) + insert into alf_audit_entry (audit_app_id, audit_user_id, audit_time, audit_values_id) + values (#auditApplicationId#, #auditUserId#, #auditTime#, #auditValuesId#) @@ -77,4 +77,14 @@ content_crc = #contentCrc# + + + \ No newline at end of file diff --git a/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/audit-insert-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/audit-insert-SqlMap.xml index 3559481dd7..c7c0d796c7 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/audit-insert-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/audit-insert-SqlMap.xml @@ -13,8 +13,8 @@ - - + + KEY_COLUMN:GENERATED_KEY diff --git a/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java b/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java index 6248f53778..38a0f329d2 100644 --- a/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java +++ b/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java @@ -31,7 +31,6 @@ 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; @@ -110,7 +109,7 @@ public class AuditBootstrapTest extends TestCase loadBadModel("classpath:alfresco/audit/alfresco-audit-test-bad-04.xml"); } - public void testModelLoading_InvalidScope() throws Exception + public void testModelLoading_InvalidDataGeneratorName() throws Exception { loadBadModel("classpath:alfresco/audit/alfresco-audit-test-bad-05.xml"); } @@ -120,10 +119,10 @@ public class AuditBootstrapTest extends TestCase loadBadModel("classpath:alfresco/audit/alfresco-audit-test-bad-06.xml"); } - public void testGetModelId() + public void testGetApplicationId() { - Long repoId = auditModelRegistry.getAuditModelId(APPLICATION_TEST); - assertNotNull("No audit model ID for " + APPLICATION_TEST, repoId); + Long appId = auditModelRegistry.getAuditApplicationId(APPLICATION_TEST); + assertNotNull("No audit application ID for " + APPLICATION_TEST, appId); } private void testBadPath(AuditApplication app, String path) @@ -162,32 +161,32 @@ public class AuditBootstrapTest extends TestCase 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")); + 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")); } - public void testAuditApplication_GetDataGenerators_AnyScope() + public void testAuditApplication_GetDataGenerators() { AuditApplication app = auditModelRegistry.getAuditApplication(APPLICATION_TEST); assertNotNull(app); - Map generators = app.getDataGenerators("/blah", DataGeneratorScope.ALL); + Map generators = app.getDataGenerators("/blah"); 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); + generators = app.getDataGenerators("/test/1.1/2.1/3.1/4.1"); assertEquals(1, generators.size()); assertTrue(generators.containsKey("/test/time")); - generators = app.getDataGenerators("/test/1.1/2.1/3.1/4.1", DataGeneratorScope.SESSION); + generators = app.getDataGenerators("/test/1.1/2.1/3.1/4.1"); 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); + generators = app.getDataGenerators("/test/1.1/2.2/3.2/4.1"); assertEquals(2, generators.size()); assertTrue(generators.containsKey("/test/time")); assertTrue(generators.containsKey("/test/1.1/2.2/3.2/4.1/time")); diff --git a/source/java/org/alfresco/repo/audit/AuditComponent.java b/source/java/org/alfresco/repo/audit/AuditComponent.java index 0a269b4f22..f1256c988d 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponent.java +++ b/source/java/org/alfresco/repo/audit/AuditComponent.java @@ -83,38 +83,6 @@ public interface AuditComponent * V3.2 from here on. Put all fixes to the older audit code before this point, please. */ - /** - * Start an audit session for the given root path. All later audit values must start with - * the same root path. - *

- * The name of the application controls part of the audit model will be used. The root path must - * start with the path separator '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) and the matching - * key attribute that was declared for the Application element in the audit configuration. - *

- * This is a read-write method. Client code must wrap calls in the appropriate transactional wrappers. - * - * @param applicationName the name of the application to log against - * @param rootPath a base path of {@link AuditPath} key entries concatenated with - * {@link AuditApplication#AUDIT_PATH_SEPARATOR}. - * @return Returns the unique session or null if no session was created - * @throws IllegalStateException if there is not a writable transaction present - */ - AuditSession startAuditSession(String applicationName, String rootPath); - - /** - * {@inheritDoc AuditComponent#startAuditSession(String, String)} - - * @param applicationName the name of the application to log against - * @param rootPath a base path of {@link AuditPath} key entries concatenated with the path separator - * '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) - * @param values values to associate with the session. These values will override or - * complement generated session-specific values - * @param rootPath a base path of {@link AuditPath} key entries concatenated with - * {@link AuditApplication#AUDIT_PATH_SEPARATOR}. - * @throws IllegalStateException if there is not a writable transaction present - */ - AuditSession startAuditSession(String applicationName, String rootPath, Map values); - /** * Record a set of values against the given session. The map is a path - starting with '/' * ({@link AuditApplication#AUDIT_PATH_SEPARATOR}), relative to the root path given when @@ -126,9 +94,11 @@ public interface AuditComponent *

* This is a read-write method. Client code must wrap calls in the appropriate transactional wrappers. * - * @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 + * @param applicationName the name of the application to log against + * @param rootPath a base path of {@link AuditPath} key entries concatenated with the path separator + * '/' ({@link AuditApplication#AUDIT_PATH_SEPARATOR}) + * @param values the values to audit mapped by {@link AuditPath} key relative to root path + * (may be null) * @return Returns the values that were actually persisted, keyed by their full path. * @throws IllegalStateException if there is not a writable transaction present * @@ -136,5 +106,5 @@ public interface AuditComponent * * @since 3.2 */ - Map audit(AuditSession session, Map values); + Map audit(String applicationName, String rootPath, Map values); } diff --git a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java index b6a01dfddf..5969668180 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java +++ b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java @@ -28,6 +28,7 @@ import java.io.Serializable; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -35,7 +36,6 @@ import java.util.Map; 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.AuditEntry; import org.alfresco.repo.audit.model.AuditModelRegistry; @@ -767,27 +767,25 @@ public class AuditComponentImpl implements AuditComponent this.auditModelRegistry = auditModelRegistry; } - /** - * @see #startAuditSession(String, String, Map) - * @since 3.2 - */ - public AuditSession startAuditSession(String applicationName, String rootPath) - { - return startAuditSession(applicationName, rootPath, new HashMap(11)); - } /** * {@inheritDoc} * @since 3.2 */ - public AuditSession startAuditSession(String applicationName, String rootPath, Map values) + public Map audit(String applicationName, String rootPath, Map values) { ParameterCheck.mandatory("applicationName", applicationName); - ParameterCheck.mandatory("values", values); + ParameterCheck.mandatory("rootPath", rootPath); if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_READ_WRITE) { throw new IllegalStateException("Auditing requires a read-write transaction."); } + + if (values == null) + { + values = Collections.emptyMap(); + } + // Get the application AuditApplication application = auditModelRegistry.getAuditApplication(applicationName); if (application == null) @@ -796,61 +794,19 @@ public class AuditComponentImpl implements AuditComponent { logger.debug("No audit application named '" + applicationName + "' has been registered."); } - return null; + return Collections.emptyMap(); } // Check the path against the application application.checkPath(rootPath); // Get the model ID for the application - Long modelId = auditModelRegistry.getAuditModelId(applicationName); - if (modelId == null) + Long applicationId = auditModelRegistry.getAuditApplicationId(applicationName); + if (applicationId == null) { - throw new AuditException("No model exists for audit application: " + applicationName); + throw new AuditException("No persisted instance exists for audit application: " + applicationName); } - // Now create the session - Long sessionId = auditDAO.createAuditSession(modelId, applicationName); - AuditSession session = new AuditSession(application, rootPath, sessionId); - - // Generate session data - Map generators = application.getDataGenerators(rootPath, DataGeneratorScope.SESSION); - Map sessionData = generateData(generators); + // TODO: Check if the root path is enabled or not - // Extract data from the values passed in - Map extractedData = extractData(application, values); - - // Combine the values - Map allData = new HashMap(sessionData); - allData.putAll(extractedData); - - // Audit it - audit(session, allData); - - // Done - if (logger.isDebugEnabled()) - { - logger.debug("New audit session: " + session); - } - return session; - } - - /** - * {@inheritDoc} - * @since 3.2 - */ - public Map audit(AuditSession session, Map values) - { - ParameterCheck.mandatory("session", session); - ParameterCheck.mandatory("values", values); - - if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_READ_WRITE) - { - throw new IllegalStateException("Auditing requires a read-write transaction."); - } - - AuditApplication app = session.getApplication(); - String rootPath = session.getRootPath(); - Long sessionId = session.getSessionId(); - // Build the key paths using the session root path Map pathedValues = new HashMap(values.size() * 2); for (Map.Entry entry : values.entrySet()) @@ -860,27 +816,38 @@ public class AuditComponentImpl implements AuditComponent pathedValues.put(path, entry.getValue()); } + // Generate data + Map generators = application.getDataGenerators(pathedValues.keySet()); + Map auditData = generateData(generators); + // Now extract values - Map extractedValues = extractData(app, pathedValues); + Map extractedData = extractData(application, pathedValues); + + // Combine extracted and generated values (extracted data takes precedence) + auditData.putAll(extractedData); // Time and username are intrinsic long time = System.currentTimeMillis(); String username = AuthenticationUtil.getFullyAuthenticatedUser(); - // Persist the values - Long entryId = auditDAO.createAuditEntry(sessionId, time, username, pathedValues); + Long entryId = null; + if (!auditData.isEmpty()) + { + // Persist the values + entryId = auditDAO.createAuditEntry(applicationId, time, username, auditData); + } // Done if (logger.isDebugEnabled()) { logger.debug( "New audit entry: \n" + - " Session ID: " + sessionId + "\n" + - " Entry ID: " + entryId + "\n" + - " Path Values: " + pathedValues + "\n" + - " Extracted Values: " + extractedValues); + " Applicatoin ID: " + applicationId + "\n" + + " Entry ID: " + entryId + "\n" + + " Path Values: " + pathedValues + "\n" + + " Audit Data: " + auditData); } - return extractedValues; + return auditData; } /** diff --git a/source/java/org/alfresco/repo/audit/AuditComponentTest.java b/source/java/org/alfresco/repo/audit/AuditComponentTest.java index 7b78b2fa5b..74ea8d6a25 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponentTest.java +++ b/source/java/org/alfresco/repo/audit/AuditComponentTest.java @@ -110,11 +110,11 @@ public class AuditComponentTest extends TestCase // Just here to fail if the basic startup fails } - public void testStartSessionWithBadPath() throws Exception + public void testAuditWithBadPath() throws Exception { try { - auditComponent.startAuditSession(APPLICATION_TEST, "test"); + auditComponent.audit(APPLICATION_TEST, "/test", null); fail("Should fail due to lack of a transaction."); } catch (IllegalStateException e) @@ -127,7 +127,7 @@ public class AuditComponentTest extends TestCase { try { - auditComponent.startAuditSession(APPLICATION_TEST, "test"); + auditComponent.audit(APPLICATION_TEST, "test", null); fail("Failed to detect illegal path"); } catch (AuditModelException e) @@ -136,18 +136,16 @@ public class AuditComponentTest extends TestCase } try { - auditComponent.startAuditSession(APPLICATION_TEST, "/test/"); + auditComponent.audit(APPLICATION_TEST, "/test/", null); fail("Failed to detect illegal path"); } catch (AuditModelException e) { // Expected } - AuditSession session; - session = auditComponent.startAuditSession("Bogus App", "/test"); - assertNull("Invalid app should return null session.", session); - session = auditComponent.startAuditSession(APPLICATION_TEST, "/test"); - assertNotNull("Valid app and root path failed to create session.", session); + Map auditedValues = auditComponent.audit("Bogus App", "/test", null); + assertNotNull(auditedValues); + assertTrue("Invalid application should not audit anything", auditedValues.isEmpty()); return null; } @@ -158,20 +156,18 @@ public class AuditComponentTest extends TestCase /** * Start a session and use it within a single txn */ - public void testSession_Basic() throws Exception + public void testAudit_Basic() throws Exception { final RetryingTransactionCallback testCallback = new RetryingTransactionCallback() { public Void execute() throws Throwable { - AuditSession session = auditComponent.startAuditSession(APPLICATION_TEST, "/test/1.1"); - Map values = new HashMap(13); values.put("/test/1.1/2.1/3.1/4.1", new Long(41)); values.put("/test/1.1/2.1/3.1/4.2", "42"); values.put("/test/1.1/2.1/3.1/4.2", new Date()); - auditComponent.audit(session, values); + auditComponent.audit(APPLICATION_TEST, "/test/1.1", values); return null; } @@ -198,7 +194,7 @@ public class AuditComponentTest extends TestCase for (Map.Entry entry : parameters.entrySet()) { String paramName = entry.getKey(); - String path = AuditApplication.buildPath("params", paramName); + String path = AuditApplication.buildPath(action, "params", paramName); adjustedValues.put(path, entry.getValue()); } @@ -207,10 +203,9 @@ public class AuditComponentTest extends TestCase { public Map execute() throws Throwable { - String actionPath = AuditApplication.buildPath("actions-test/actions", action); - AuditSession session = auditComponent.startAuditSession(APPLICATION_ACTIONS_TEST, actionPath); + String actionPath = AuditApplication.buildPath("actions-test/actions"); - return auditComponent.audit(session, adjustedValues); + return auditComponent.audit(APPLICATION_ACTIONS_TEST, actionPath, adjustedValues); } }; return transactionService.getRetryingTransactionHelper().doInTransaction(auditCallback); @@ -279,7 +274,8 @@ public class AuditComponentTest extends TestCase Map result = auditTestAction("action-01", nodeRef, parameters); Map expected = new HashMap(); - expected.put("/actions-test/actions/action-01/context-node/noderef", nodeRef); + expected.put("/actions-test/actions/user", AuthenticationUtil.getFullyAuthenticatedUser()); + expected.put("/actions-test/actions/context-node/noderef", nodeRef); expected.put("/actions-test/actions/action-01/params/A/value", valueA); expected.put("/actions-test/actions/action-01/params/B/value", valueB); expected.put("/actions-test/actions/action-01/params/C/value", valueC); diff --git a/source/java/org/alfresco/repo/audit/AuditEntry.java b/source/java/org/alfresco/repo/audit/AuditEntry.java new file mode 100644 index 0000000000..50bc026618 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditEntry.java @@ -0,0 +1,93 @@ +/* + * 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; + +import java.io.Serializable; +import java.util.Date; +import java.util.Map; + +import org.alfresco.util.ParameterCheck; + +/** + * Bean to hold audit entry data. An audit entry represents a single audit call, but the + * data stored may be a large map. + * + * @author Derek Hulley + * @since 3.2 + */ +public class AuditEntry +{ + private final String user; + private final long time; + private final Long valuesId; + private final Map values; + + /** + * TODO: Comment + */ + public AuditEntry(String user, long time, Long valuesId, Map values) + { + ParameterCheck.mandatoryString("user", user); + ParameterCheck.mandatory("time", time); + + this.user = user; + this.time = time; + this.valuesId = valuesId; + this.values = values; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(512); + sb.append("AuditEntry") + .append("[ user=").append(user) + .append(", time=").append(new Date(time)) + .append(", valuesId=").append(valuesId) + .append(", values=").append(values) + .append("]"); + return sb.toString(); + } + + public String getUser() + { + return user; + } + + public long getTime() + { + return time; + } + + public Long getValuesId() + { + return valuesId; + } + + public Map getValues() + { + return values; + } +} diff --git a/source/java/org/alfresco/repo/audit/AuditServiceImpl.java b/source/java/org/alfresco/repo/audit/AuditServiceImpl.java index cd4487b7e2..e45d4a61cc 100644 --- a/source/java/org/alfresco/repo/audit/AuditServiceImpl.java +++ b/source/java/org/alfresco/repo/audit/AuditServiceImpl.java @@ -122,4 +122,16 @@ public class AuditServiceImpl implements AuditService tx.commit(); } + + /* + * V3.2 from here on. Put all fixes to the older audit code before this point, please. + */ + + /** + * {@inheritDoc} + */ + public void auditQuery(AuditQueryCallback callback, String auditPath, String user, long from, long to, int limit) + { + throw new UnsupportedOperationException(); + } } diff --git a/source/java/org/alfresco/repo/audit/AuditSession.java b/source/java/org/alfresco/repo/audit/AuditSession.java deleted file mode 100644 index c733614740..0000000000 --- a/source/java/org/alfresco/repo/audit/AuditSession.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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; - -import org.alfresco.repo.audit.model.AuditApplication; -import org.alfresco.util.ParameterCheck; - -/** - * Bean to hold session information for repeated use. - * - * @author Derek Hulley - * @since 3.2 - */ -public class AuditSession -{ - private final AuditApplication application; - private final String rootPath; - private final Long sessionId; - - /** - * @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(AuditApplication application, String rootPath, Long sessionId) - { - ParameterCheck.mandatory("application", application); - ParameterCheck.mandatoryString("rootPath", rootPath); - ParameterCheck.mandatory("sessionId", sessionId); - - this.application = application; - this.rootPath = rootPath; - this.sessionId = sessionId; - } - - @Override - public int hashCode() - { - return (application.hashCode() + rootPath.hashCode()); - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj instanceof AuditSession) - { - AuditSession that = (AuditSession) obj; - return this.application.equals(that.application) && - this.rootPath.equals(that.rootPath); - } - else - { - return false; - } - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(512); - sb.append("AuditSession") - .append("[ application=").append(application) - .append(", rootPath=").append(rootPath) - .append(", sessionId=").append(sessionId) - .append("]"); - return sb.toString(); - } - - public AuditApplication getApplication() - { - return application; - } - - public String getRootPath() - { - return rootPath; - } - - public Long getSessionId() - { - return sessionId; - } -} diff --git a/source/java/org/alfresco/repo/audit/generator/DataGenerator.java b/source/java/org/alfresco/repo/audit/generator/DataGenerator.java index 9a35bc6b29..b62f33c905 100644 --- a/source/java/org/alfresco/repo/audit/generator/DataGenerator.java +++ b/source/java/org/alfresco/repo/audit/generator/DataGenerator.java @@ -38,16 +38,6 @@ import java.io.Serializable; */ public interface DataGenerator { - public static enum DataGeneratorScope - { - /** Data is generated for new audit sessions only */ - SESSION, - /** Data is generated for individual audit calls only */ - AUDIT, - /** Data is generated for new audit sessions and audit calls */ - ALL - } - /** * Get the data generated by the instance. * diff --git a/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java b/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java index f32f48dd7a..d64d2f9709 100644 --- a/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java +++ b/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java @@ -140,17 +140,6 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, this.localSessionFactory = localSessionFactory; } - /** - * Fallout implementation from new audit DAO - * - * @throws UnsupportedOperationException always - * @since 3.2 - */ - public Long createAuditSession(Long modelId, String application) - { - throw new UnsupportedOperationException(); - } - /** * Fallout implementation from new audit DAO * @@ -168,7 +157,18 @@ public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, * @throws UnsupportedOperationException always * @since 3.2 */ - public Long createAuditEntry(Long sessionId, long time, String username, Map values) + public Long getOrCreateAuditApplication(Long modelId, String applicationName) + { + throw new UnsupportedOperationException(); + } + + /** + * Fallout implementation from new audit DAO + * + * @throws UnsupportedOperationException always + * @since 3.2 + */ + public Long createAuditEntry(Long applicationId, long time, String username, Map values) { throw new UnsupportedOperationException(); } diff --git a/source/java/org/alfresco/repo/audit/model/AuditApplication.java b/source/java/org/alfresco/repo/audit/model/AuditApplication.java index 8c41b76b93..bdeabc7a59 100644 --- a/source/java/org/alfresco/repo/audit/model/AuditApplication.java +++ b/source/java/org/alfresco/repo/audit/model/AuditApplication.java @@ -27,18 +27,15 @@ 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; @@ -70,8 +67,6 @@ public class AuditApplication private Map> dataExtractors = new HashMap>(11); /** Derived expaned map for fast lookup */ private Map> dataGenerators = new HashMap>(11); - /** Derived expaned map for fast lookup */ - private Map dataGeneratorScopes = new HashMap(11); /** * @param application the application that will be wrapped @@ -251,43 +246,29 @@ public class AuditApplication * Get all data generators applicable to a given path and scope. * * @param path the audit path - * @param scope the audit scope (e.g. SESSION or AUDIT) * @return Returns all data generators mapped to their key-path */ - public Map getDataGenerators(String path, DataGeneratorScope scope) + public Map getDataGenerators(String path) { - Map generators = dataGenerators.get(path); - if (generators == null) + return getDataGenerators(Collections.singleton(path)); + } + + /** + * Get all data generators applicable to a given path and scope. + * + * @param paths the audit paths + * @return Returns all data generators mapped to their key-path + */ + public Map getDataGenerators(Set paths) + { + Map amalgamatedGenerators = new HashMap(13); + for (String path : paths) { - // Don't give back a null - generators = new HashMap(0); - } - else - { - // Copy the map so that (a) we can modify it during iteration and (b) we return - // something that the client can't mess up. - generators = new HashMap(generators); - } - - if (scope != DataGeneratorScope.ALL) - { - // Go through them and eliminate the ones in the wrong scope - Iterator> iterator = generators.entrySet().iterator(); - while (iterator.hasNext()) + Map generators = dataGenerators.get(path); + if (generators != null) { - Map.Entry entry = iterator.next(); - String generatorPath = entry.getKey(); - DataGeneratorScope generatorScope = dataGeneratorScopes.get(generatorPath); - if (generatorScope == DataGeneratorScope.ALL) - { - // This one always applies - continue; - } - else if (generatorScope != scope) - { - // Wrong scope - iterator.remove(); - } + // Copy values to combined map + amalgamatedGenerators.putAll(generators); } } @@ -296,11 +277,10 @@ public class AuditApplication { logger.debug( "Looked up data generators: \n" + - " Path: " + path + "\n" + - " Scope: " + scope + "\n" + - " Found: " + generators); + " Paths: " + paths + "\n" + + " Found: " + amalgamatedGenerators); } - return generators; + return amalgamatedGenerators; } /** @@ -365,11 +345,11 @@ public class AuditApplication } // 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.clear(); + // 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()) @@ -387,26 +367,6 @@ public class AuditApplication { 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); } diff --git a/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java b/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java index 0ddae8201a..338c303747 100644 --- a/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java +++ b/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java @@ -96,9 +96,9 @@ public class AuditModelRegistry */ private final Map auditApplicationsByName; /** - * Used to lookup a reference to the persisted config binary for an application + * Used to lookup a reference to the application */ - private final Map auditModelIdsByApplicationsName; + private final Map auditApplicationIdsByApplicationsName; /** * Default constructor @@ -114,7 +114,7 @@ public class AuditModelRegistry auditModelUrls = new HashSet(7); auditModels = new ArrayList(7); auditApplicationsByName = new HashMap(7); - auditModelIdsByApplicationsName = new HashMap(7); + auditApplicationIdsByApplicationsName = new HashMap(7); } /** @@ -209,7 +209,7 @@ public class AuditModelRegistry { auditModels.clear(); auditApplicationsByName.clear(); - auditModelIdsByApplicationsName.clear(); + auditApplicationIdsByApplicationsName.clear(); } /** @@ -271,17 +271,17 @@ public class AuditModelRegistry } /** - * Get the ID of the persisted audit model for the given application name + * Get the ID of the persisted audit application for the given application name * * @param applicationName the name of the audited application - * @return the unique ID of the persisted model (null if not found) + * @return the unique ID of the persisted application (null if not found) */ - public Long getAuditModelId(String applicationName) + public Long getAuditApplicationId(String applicationName) { readLock.lock(); try { - return auditModelIdsByApplicationsName.get(applicationName); + return auditApplicationIdsByApplicationsName.get(applicationName); } finally { @@ -514,9 +514,13 @@ public class AuditModelRegistry { throw new AuditModelException("Audit application '" + name + "' has already been defined."); } + + // Get the ID of the application + Long appId = auditDAO.getOrCreateAuditApplication(auditModelId, name); + AuditApplication wrapperApp = new AuditApplication(dataExtractorsByName, dataGeneratorsByName, application); auditApplicationsByName.put(name, wrapperApp); - auditModelIdsByApplicationsName.put(name, auditModelId); + auditApplicationIdsByApplicationsName.put(name, appId); } // Store the model itself auditModels.add(audit); diff --git a/source/java/org/alfresco/repo/audit/model/_3/GenerateValue.java b/source/java/org/alfresco/repo/audit/model/_3/GenerateValue.java index 60caf6d316..8fcd9206d5 100644 --- a/source/java/org/alfresco/repo/audit/model/_3/GenerateValue.java +++ b/source/java/org/alfresco/repo/audit/model/_3/GenerateValue.java @@ -17,7 +17,6 @@ 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> @@ -33,8 +32,6 @@ public class GenerateValue @XmlAttribute(required = true) protected String dataGenerator; - @XmlAttribute(required = true) - protected ScopeAttribute scope; /** * Gets the value of the dataGenerator property. @@ -60,28 +57,4 @@ public class GenerateValue this.dataGenerator = value; } - /** - * Gets the value of the scope property. - * - * @return - * possible object is - * {@link ScopeAttribute } - * - */ - public ScopeAttribute getScope() { - return scope; - } - - /** - * Sets the value of the scope property. - * - * @param value - * allowed object is - * {@link ScopeAttribute } - * - */ - public void setScope(ScopeAttribute value) { - this.scope = value; - } - } diff --git a/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java b/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java index 2828f80fd4..72c534043c 100644 --- a/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java +++ b/source/java/org/alfresco/repo/audit/model/_3/ObjectFactory.java @@ -34,11 +34,11 @@ public class ObjectFactory { } /** - * Create an instance of {@link KeyedAuditDefinition } + * Create an instance of {@link RecordValue } * */ - public KeyedAuditDefinition createKeyedAuditDefinition() { - return new KeyedAuditDefinition(); + public RecordValue createRecordValue() { + return new RecordValue(); } /** @@ -57,14 +57,6 @@ public class ObjectFactory { return new Audit(); } - /** - * Create an instance of {@link DataExtractors } - * - */ - public DataExtractors createDataExtractors() { - return new DataExtractors(); - } - /** * Create an instance of {@link AuditPath } * @@ -90,11 +82,11 @@ public class ObjectFactory { } /** - * Create an instance of {@link RecordValue } + * Create an instance of {@link KeyedAuditDefinition } * */ - public RecordValue createRecordValue() { - return new RecordValue(); + public KeyedAuditDefinition createKeyedAuditDefinition() { + return new KeyedAuditDefinition(); } /** @@ -113,6 +105,14 @@ public class ObjectFactory { return new GenerateValue(); } + /** + * Create an instance of {@link DataExtractors } + * + */ + public DataExtractors createDataExtractors() { + return new DataExtractors(); + } + /** * Create an instance of {@link JAXBElement }{@code <}{@link Audit }{@code >}} * diff --git a/source/java/org/alfresco/repo/audit/model/_3/ScopeAttribute.java b/source/java/org/alfresco/repo/audit/model/_3/ScopeAttribute.java deleted file mode 100644 index 029bcb2971..0000000000 --- a/source/java/org/alfresco/repo/audit/model/_3/ScopeAttribute.java +++ /dev/null @@ -1,40 +0,0 @@ - -package org.alfresco.repo.audit.model._3; - -import javax.xml.bind.annotation.XmlEnum; -import javax.xml.bind.annotation.XmlType; - - -/** - *

Java class for ScopeAttribute. - * - *

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

- *

- * <simpleType name="ScopeAttribute">
- *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
- *     <enumeration value="SESSION"/>
- *     <enumeration value="AUDIT"/>
- *     <enumeration value="ALL"/>
- *   </restriction>
- * </simpleType>
- * 
- * - */ -@XmlType(name = "ScopeAttribute") -@XmlEnum -public enum ScopeAttribute { - - SESSION, - AUDIT, - ALL; - - public String value() { - return name(); - } - - public static ScopeAttribute fromValue(String v) { - return valueOf(v); - } - -} diff --git a/source/java/org/alfresco/repo/domain/audit/AbstractAuditDAOImpl.java b/source/java/org/alfresco/repo/domain/audit/AbstractAuditDAOImpl.java index 211c7fde54..34ca5c054f 100644 --- a/source/java/org/alfresco/repo/domain/audit/AbstractAuditDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/audit/AbstractAuditDAOImpl.java @@ -56,7 +56,7 @@ import org.apache.commons.logging.LogFactory; */ public abstract class AbstractAuditDAOImpl implements AuditDAO { - private static final Log logger = LogFactory.getLog(AbstractAuditDAOImpl.class); + protected final Log logger = LogFactory.getLog(this.getClass()); private HibernateAuditDAO oldDAO; private ContentService contentService; @@ -83,6 +83,11 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO this.propertyValueDAO = propertyValueDAO; } + protected PropertyValueDAO getPropertyValueDAO() + { + return this.propertyValueDAO; + } + /* * Support for older audit DAO */ @@ -196,34 +201,42 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO protected abstract AuditModelEntity createAuditModel(Long contentDataId, long crc); /* - * alf_audit_session + * alf_audit_application */ - - public Long createAuditSession(Long modelId, String application) + + public Long getOrCreateAuditApplication(Long modelId, String application) { - // Persist the string - Long appNameId = propertyValueDAO.getOrCreatePropertyValue(application).getFirst(); - // Create the audit session - AuditSessionEntity entity = createAuditSession(appNameId, modelId); - // Done - if (logger.isDebugEnabled()) + // Search for it + AuditApplicationEntity entity = getAuditApplicationByModelIdAndName(modelId, application); + if (entity == null) { - logger.debug( - "Created new audit session: \n" + - " Model: " + modelId + "\n" + - " App: " + application + "\n" + - " Result: " + entity); + // Create it + // Persist the string + Long appNameId = propertyValueDAO.getOrCreatePropertyValue(application).getFirst(); + // Create the audit session + entity = createAuditApplication(modelId, appNameId); + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Created new audit application: \n" + + " Model: " + modelId + "\n" + + " App: " + application + "\n" + + " Result: " + entity); + } } + // Done return entity.getId(); } - - protected abstract AuditSessionEntity createAuditSession(Long appNameId, Long modelId); + + protected abstract AuditApplicationEntity getAuditApplicationByModelIdAndName(Long modelId, String appName); + protected abstract AuditApplicationEntity createAuditApplication(Long modelId, Long appNameId); /* * alf_audit_entry */ - public Long createAuditEntry(Long sessionId, long time, String username, Map values) + public Long createAuditEntry(Long applicationId, long time, String username, Map values) { final Long usernameId; if (username != null) @@ -242,20 +255,20 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO } // Create the audit entry - AuditEntryEntity entity = createAuditEntry(sessionId, time, usernameId, valuesId); + AuditEntryEntity entity = createAuditEntry(applicationId, time, usernameId, valuesId); // Done if (logger.isDebugEnabled()) { logger.debug( "Created new audit entry: \n" + - " Session: " + sessionId + "\n" + - " Time: " + (new Date(time)) + "\n" + - " User: " + username + "\n" + - " Result: " + entity); + " Application: " + applicationId + "\n" + + " Time: " + (new Date(time)) + "\n" + + " User: " + username + "\n" + + " Result: " + entity); } return entity.getId(); } - protected abstract AuditEntryEntity createAuditEntry(Long sessionId, long time, Long usernameId, Long valuesId); + protected abstract AuditEntryEntity createAuditEntry(Long applicationId, long time, Long usernameId, Long valuesId); } diff --git a/source/java/org/alfresco/repo/domain/audit/AuditSessionEntity.java b/source/java/org/alfresco/repo/domain/audit/AuditApplicationEntity.java similarity index 89% rename from source/java/org/alfresco/repo/domain/audit/AuditSessionEntity.java rename to source/java/org/alfresco/repo/domain/audit/AuditApplicationEntity.java index 3036891da3..4205fb8f62 100644 --- a/source/java/org/alfresco/repo/domain/audit/AuditSessionEntity.java +++ b/source/java/org/alfresco/repo/domain/audit/AuditApplicationEntity.java @@ -25,18 +25,18 @@ package org.alfresco.repo.domain.audit; /** - * Entity bean for alf_audit_session table. + * Entity bean for alf_audit_application table. * * @author Derek Hulley * @since 3.2 */ -public class AuditSessionEntity +public class AuditApplicationEntity { private Long id; - private Long applicationNameId; private Long auditModelId; + private Long applicationNameId; - public AuditSessionEntity() + public AuditApplicationEntity() { } @@ -44,10 +44,10 @@ public class AuditSessionEntity public String toString() { StringBuilder sb = new StringBuilder(512); - sb.append("AuditSessionEntity") + sb.append("AuditApplicationEntity") .append("[ ID=").append(id) - .append(", applicationNameId=").append(applicationNameId) .append(", auditModelId=").append(auditModelId) + .append(", applicationNameId=").append(applicationNameId) .append("]"); return sb.toString(); } diff --git a/source/java/org/alfresco/repo/domain/audit/AuditDAO.java b/source/java/org/alfresco/repo/domain/audit/AuditDAO.java index aa2d923095..983edd27c0 100644 --- a/source/java/org/alfresco/repo/domain/audit/AuditDAO.java +++ b/source/java/org/alfresco/repo/domain/audit/AuditDAO.java @@ -75,22 +75,22 @@ public interface AuditDAO Pair getOrCreateAuditModel(URL url); /** - * Creates a new audit session entry - there is no session re-use. + * Creates a new audit application or finds an existing one * - * @param modelId an existing audit model ID - * @param application the name of the application - * @return Returns the unique session ID + * @param modelId the ID of the model configuration + * @param applicationName the name of the application + * @return Returns the ID of the application entry */ - Long createAuditSession(Long modelId, String application); + Long getOrCreateAuditApplication(Long modelId, String applicationName); /** * Create a new audit entry with the given map of values. * - * @param sessionId an existing audit session ID + * @param applicationId an existing audit application ID * @param time the time (ms since epoch) to log the entry against * @param username the authenticated user (null if not present) * @param values the values to record * @return Returns the unique entry ID */ - Long createAuditEntry(Long sessionId, long time, String username, Map values); -} + Long createAuditEntry(Long applicationId, long time, String username, Map values); +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/domain/audit/AuditDAOTest.java b/source/java/org/alfresco/repo/domain/audit/AuditDAOTest.java index bd23769c3d..5dda720e64 100644 --- a/source/java/org/alfresco/repo/domain/audit/AuditDAOTest.java +++ b/source/java/org/alfresco/repo/domain/audit/AuditDAOTest.java @@ -88,7 +88,7 @@ public class AuditDAOTest extends TestCase assertEquals(configPair, configPairCheck); } - public void testAuditSession() throws Exception + public void testAuditApplicatoin() throws Exception { final File file = AbstractContentTransformerTest.loadQuickTestFile("pdf"); assertNotNull(file); @@ -104,22 +104,22 @@ public class AuditDAOTest extends TestCase final String appName = getName() + "." + System.currentTimeMillis(); final int count = 1000; - RetryingTransactionCallback createSessionCallback = new RetryingTransactionCallback() + RetryingTransactionCallback createAppCallback = new RetryingTransactionCallback() { public Void execute() throws Throwable { for (int i = 0; i < count; i++) { - auditDAO.createAuditSession(modelId, appName); + auditDAO.getOrCreateAuditApplication(modelId, appName); } return null; } }; long before = System.nanoTime(); - txnHelper.doInTransaction(createSessionCallback); + txnHelper.doInTransaction(createAppCallback); long after = System.nanoTime(); System.out.println( - "Time for " + count + " session creations was " + + "Time for " + count + " application creations was " + ((double)(after - before)/(10E6)) + "ms"); } @@ -130,15 +130,15 @@ public class AuditDAOTest extends TestCase final URL url = new URL("file:" + file.getAbsolutePath()); final String appName = getName() + "." + System.currentTimeMillis(); - RetryingTransactionCallback createSessionCallback = new RetryingTransactionCallback() + RetryingTransactionCallback createAppCallback = new RetryingTransactionCallback() { public Long execute() throws Throwable { Long modelId = auditDAO.getOrCreateAuditModel(url).getFirst(); - return auditDAO.createAuditSession(modelId, appName); + return auditDAO.getOrCreateAuditApplication(modelId, appName); } }; - final Long sessionId = txnHelper.doInTransaction(createSessionCallback); + final Long sessionId = txnHelper.doInTransaction(createAppCallback); final int count = 1000; final String username = "alexi"; diff --git a/source/java/org/alfresco/repo/domain/audit/AuditEntryEntity.java b/source/java/org/alfresco/repo/domain/audit/AuditEntryEntity.java index f289d1758a..dd0275f658 100644 --- a/source/java/org/alfresco/repo/domain/audit/AuditEntryEntity.java +++ b/source/java/org/alfresco/repo/domain/audit/AuditEntryEntity.java @@ -35,7 +35,7 @@ import java.util.Date; public class AuditEntryEntity { private Long id; - private Long auditSessionId; + private Long auditApplicationId; private Long auditUserId; private long auditTime; private Long auditValuesId; @@ -50,7 +50,7 @@ public class AuditEntryEntity StringBuilder sb = new StringBuilder(512); sb.append("AuditEntryEntity") .append("[ ID=").append(id) - .append(", auditSessionId=").append(auditSessionId) + .append(", auditApplicationId=").append(auditApplicationId) .append(", auditTime").append(new Date(auditTime)) .append(", auditValuesId=").append(auditValuesId) .append("]"); @@ -67,14 +67,14 @@ public class AuditEntryEntity this.id = id; } - public Long getAuditSessionId() + public Long getAuditApplicationId() { - return auditSessionId; + return auditApplicationId; } - public void setAuditSessionId(Long auditSessionId) + public void setAuditApplicationId(Long auditSessionId) { - this.auditSessionId = auditSessionId; + this.auditApplicationId = auditSessionId; } public Long getAuditUserId() diff --git a/source/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java b/source/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java index c293eedac7..78b96bbca2 100644 --- a/source/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java @@ -24,10 +24,16 @@ */ package org.alfresco.repo.domain.audit.ibatis; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.alfresco.repo.domain.audit.AbstractAuditDAOImpl; +import org.alfresco.repo.domain.audit.AuditApplicationEntity; import org.alfresco.repo.domain.audit.AuditEntryEntity; import org.alfresco.repo.domain.audit.AuditModelEntity; -import org.alfresco.repo.domain.audit.AuditSessionEntity; +import org.alfresco.util.Pair; import org.springframework.orm.ibatis.SqlMapClientTemplate; /** @@ -41,7 +47,8 @@ public class AuditDAOImpl extends AbstractAuditDAOImpl private static final String SELECT_MODEL_BY_CRC = "select.AuditModelByCrc"; private static final String INSERT_MODEL = "insert.AuditModel"; - private static final String INSERT_SESSION = "insert.AuditSession"; + private static final String SELECT_APPLICATION_BY_MODEL_ID = "select.AuditApplicationByModelId"; + private static final String INSERT_APPLICATION = "insert.AuditApplication"; private static final String INSERT_ENTRY = "insert.AuditEntry"; @@ -75,22 +82,59 @@ public class AuditDAOImpl extends AbstractAuditDAOImpl return entity; } + @SuppressWarnings("unchecked") @Override - protected AuditSessionEntity createAuditSession(Long appNameId, Long modelId) + protected AuditApplicationEntity getAuditApplicationByModelIdAndName(Long modelId, String appName) { - AuditSessionEntity entity = new AuditSessionEntity(); - entity.setApplicationNameId(appNameId); + Map params = new HashMap(11); + params.put("id", modelId); + List results = (List) template.queryForList( + SELECT_APPLICATION_BY_MODEL_ID, + params); + // There could be multiple hits for the model ID. Go through them and find the correct app name. + AuditApplicationEntity result = null; + for (AuditApplicationEntity row : results) + { + Long appNameId = row.getApplicationNameId(); + Pair propPair = getPropertyValueDAO().getPropertyValueById(appNameId); + if (propPair == null) + { + // There is a FK to protect against this, but we'll just log it + logger.warn("An audit application references a non-existent app_name_id: " + appNameId); + } + // Check for exact match + Serializable propValue = propPair.getSecond(); + if (propValue instanceof String && propValue.equals(appName)) + { + // Got it + result = row; + break; + } + } + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Searched for audit application with model id " + modelId + " and found: " + result); + } + return result; + } + + @Override + protected AuditApplicationEntity createAuditApplication(Long modelId, Long appNameId) + { + AuditApplicationEntity entity = new AuditApplicationEntity(); entity.setAuditModelId(modelId); - Long id = (Long) template.insert(INSERT_SESSION, entity); + entity.setApplicationNameId(appNameId); + Long id = (Long) template.insert(INSERT_APPLICATION, entity); entity.setId(id); return entity; } @Override - protected AuditEntryEntity createAuditEntry(Long sessionId, long time, Long usernameId, Long valuesId) + protected AuditEntryEntity createAuditEntry(Long applicationId, long time, Long usernameId, Long valuesId) { AuditEntryEntity entity = new AuditEntryEntity(); - entity.setAuditSessionId(sessionId); + entity.setAuditApplicationId(applicationId); entity.setAuditTime(time); entity.setAuditUserId(usernameId); entity.setAuditValuesId(valuesId); diff --git a/source/java/org/alfresco/service/cmr/audit/AuditService.java b/source/java/org/alfresco/service/cmr/audit/AuditService.java index b94704815b..9275238d12 100644 --- a/source/java/org/alfresco/service/cmr/audit/AuditService.java +++ b/source/java/org/alfresco/service/cmr/audit/AuditService.java @@ -24,7 +24,9 @@ */ package org.alfresco.service.cmr.audit; +import java.io.Serializable; import java.util.List; +import java.util.Map; import org.alfresco.service.NotAuditable; import org.alfresco.service.PublicService; @@ -100,4 +102,62 @@ public interface AuditService */ @NotAuditable public List getAuditTrail(NodeRef nodeRef); + + /* + * V3.2 from here on. Put all fixes to the older audit code before this point, please. + */ + + /** + * The interface that will be used to give query results to the calling code. + * @since 3.2 + */ + public static interface AuditQueryCallback + { + /** + * Check if summary or full data fetching is required. Depending on the return value, + * the underlying query may be completely different; it is not possible to change the + * return value and expect the callback to be used differently during row handling. + * + * @return Return true if summary data is required only i.e + * the full map of audit values for the entries will not be + * retrieved. + */ + boolean isSummaryOnly(); + + /** + * Handle a summary row of audit entry data. The ID of the full values map is provided. + * + * @param applicationName the name of the application + * @param user the user that logged the entry + * @param time the time of the entry + * @param valuesId the ID of the values map as created + * @return Return true to continue processing rows or false to stop + */ + boolean handleAuditEntrySummary(String applicationName, String user, long time, Long valuesId); + + /** + * Handle a full row of audit entry data. + * + * @param applicationName the name of the application + * @param user the user that logged the entry + * @param time the time of the entry + * @param values the values map as created + * @return Return true to continue processing rows or false to stop + */ + boolean handleAuditEntryFull(String applicationName, String user, long time, Map values); + } + + /** + * Get the audit entries that match the given criteria. + * + * @param callback the callback that will handle results + * @param auditPath if not null, at least one value in the entry must start with this path + * @param user if not null, the entry must be logged against this user + * @param from the start search time (use 0L) to cover all times + * @param to the end search time (use Long.MAX_VALUE) to cover all times + * @param limit the maximum number of results to retrieve + */ + void auditQuery( + AuditQueryCallback callback, + String auditPath, String user, long from, long to, int limit); } diff --git a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-02.xml b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-02.xml index 6e38a361ed..1d317e6610 100644 --- a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-02.xml +++ b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-02.xml @@ -11,7 +11,7 @@ - + diff --git a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-05.xml b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-05.xml index e1e39b7070..62eea69612 100644 --- a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-05.xml +++ b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-05.xml @@ -15,7 +15,7 @@ - + diff --git a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-06.xml b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-06.xml index bd1f47ab0b..a57ca54df0 100644 --- a/source/test-resources/alfresco/audit/alfresco-audit-test-bad-06.xml +++ b/source/test-resources/alfresco/audit/alfresco-audit-test-bad-06.xml @@ -15,7 +15,7 @@ - + diff --git a/source/test-resources/alfresco/audit/alfresco-audit-test.xml b/source/test-resources/alfresco/audit/alfresco-audit-test.xml index 36f6a62bbd..1199d9183e 100644 --- a/source/test-resources/alfresco/audit/alfresco-audit-test.xml +++ b/source/test-resources/alfresco/audit/alfresco-audit-test.xml @@ -20,7 +20,7 @@ - + @@ -56,7 +56,7 @@ - + @@ -67,12 +67,12 @@ - + + + + - - -