diff --git a/config/alfresco/audit-services-context.xml b/config/alfresco/audit-services-context.xml index c91e2e877d..b2b94d1f22 100644 --- a/config/alfresco/audit-services-context.xml +++ b/config/alfresco/audit-services-context.xml @@ -76,14 +76,15 @@ - + + - - + + classpath:alfresco/audit/alfresco-audit-repository.xml - + \ No newline at end of file diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index f5ce22c3cf..819b5c8b6b 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -494,6 +494,12 @@ + + + + + + diff --git a/config/alfresco/dao/dao-context.xml b/config/alfresco/dao/dao-context.xml index d9ce5ae336..3c9caac703 100644 --- a/config/alfresco/dao/dao-context.xml +++ b/config/alfresco/dao/dao-context.xml @@ -59,6 +59,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 a85996943e..712696ccd5 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 @@ -7,13 +7,23 @@ -- Please contact support@alfresco.com if you need assistance with the upgrade. -- -CREATE TABLE alf_audit_cfg +CREATE TABLE alf_audit_model ( id BIGINT NOT NULL AUTO_INCREMENT, content_data_id BIGINT NOT NULL, content_crc BIGINT NOT NULL, UNIQUE INDEX idx_alf_audit_cfg_crc (content_crc), - CONSTRAINT fk_alf_audit_cfg_cd FOREIGN KEY (content_data_id) REFERENCES alf_content_data (id), + CONSTRAINT fk_alf_audit_model_cd FOREIGN KEY (content_data_id) REFERENCES alf_content_data (id), + PRIMARY KEY (id) +) ENGINE=InnoDB; + +CREATE TABLE alf_audit_session +( + 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), PRIMARY KEY (id) ) ENGINE=InnoDB; 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 1be605727c..aa7ebcd557 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 @@ -10,17 +10,23 @@ - + + - + + + + + + @@ -34,21 +40,26 @@ - - insert into alf_audit_cfg (content_data_id, content_crc) + + insert into alf_audit_model (content_data_id, content_crc) values (#contentDataId#, #contentCrc#) + + insert into alf_audit_session (audit_model_id, app_name_id) + values (#auditModelId#, #applicationNameId#) + + - - select * from - alf_audit_cfg + alf_audit_model where content_crc = #contentCrc# 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 44a704f579..9b6026cfef 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 @@ -6,8 +6,15 @@ - - + + + + KEY_COLUMN:GENERATED_KEY + + + + + KEY_COLUMN:GENERATED_KEY diff --git a/source/java/org/alfresco/repo/audit/AuditBootstrap.java b/source/java/org/alfresco/repo/audit/AuditBootstrap.java new file mode 100644 index 0000000000..b9744bae95 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditBootstrap.java @@ -0,0 +1,78 @@ +/* + * 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.AuditModelRegistry; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.AbstractLifecycleBean; +import org.springframework.context.ApplicationEvent; + +/** + * Starts all the necessary audit functionality once the repository has started. + * + * @author Derek Hulley + * @since 3.2 + */ +public class AuditBootstrap extends AbstractLifecycleBean +{ + private TransactionService transactionService; + private AuditModelRegistry auditModelRegistry; + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setAuditModelRegistry(AuditModelRegistry registry) + { + this.auditModelRegistry = registry; + } + + /** + * @see AuditModelRegistry#loadAuditModels() + */ + @Override + protected void onBootstrap(ApplicationEvent event) + { + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + auditModelRegistry.loadAuditModels(); + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(callback); + } + + /** + * No-op + */ + @Override + protected void onShutdown(ApplicationEvent event) + { + } +} diff --git a/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java b/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java new file mode 100644 index 0000000000..6ea1b4a305 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java @@ -0,0 +1,65 @@ +/* + * 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 junit.framework.TestCase; + +import org.alfresco.repo.audit.model.AuditModelRegistry; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; + +/** + * Tests that auditing is loaded properly on repository startup. + * + * @see AuditBootstrap + * + * @author Derek Hulley + * @since 3.2 + */ +public class AuditBootstrapTest extends TestCase +{ + private static final String APPLICATION_REPOSITORY = "Alfresco Repository"; + + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + private AuditModelRegistry auditModelRegistry; + + @Override + public void setUp() throws Exception + { + auditModelRegistry = (AuditModelRegistry) ctx.getBean("auditModel.registry"); + } + + public void testSetUp() + { + // Just here to fail if the basic startup fails + } + + public void testGetModelId() + { + Long repoId = auditModelRegistry.getAuditModelId(APPLICATION_REPOSITORY); + assertNotNull("No audit model ID for " + APPLICATION_REPOSITORY, repoId); + } +} diff --git a/source/java/org/alfresco/repo/audit/AuditComponent.java b/source/java/org/alfresco/repo/audit/AuditComponent.java index a8e8ba84f5..8ea6bec188 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponent.java +++ b/source/java/org/alfresco/repo/audit/AuditComponent.java @@ -77,14 +77,23 @@ public interface AuditComponent */ public List getAuditTrail(NodeRef nodeRef); + /* + * 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 operations on the resulting * session will be relative to this root path. + *

+ * The name of the application controls part of the audit model will be used. The root path must + * start with the matching key attribute that was declared for the matching + * Application element in the audit configuration. * + * @param application the name of the application to log against * @param rootPath a base path of {@link AuditPath} key entries concatenated with . (period) * @return Returns the unique session identifier */ - public Long startAuditSession(String rootPath); + public Long startAuditSession(String application, String rootPath); /** * Record a set of values against the given session. diff --git a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java index 35310108b0..9938929ee7 100644 --- a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java +++ b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java @@ -761,7 +761,7 @@ public class AuditComponentImpl implements AuditComponent * V3.2 from here on. Put all fixes to the older audit code before this point, please. */ - public Long startAuditSession(String rootPath) + public Long startAuditSession(String application, String rootPath) { throw new UnsupportedOperationException(); } diff --git a/source/java/org/alfresco/repo/audit/AuditSession.java b/source/java/org/alfresco/repo/audit/AuditSession.java new file mode 100644 index 0000000000..3c75e4256a --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditSession.java @@ -0,0 +1,95 @@ +/* + * 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._3.Application; +import org.alfresco.util.ParameterCheck; + +/** + * Entity bean for alf_audit_session table. + * + * @author Derek Hulley + * @since 3.2 + */ +public class AuditSession +{ + private final Application application; + private final String rootPath; + + public AuditSession(Application application, String rootPath) + { + ParameterCheck.mandatory("application", application); + ParameterCheck.mandatoryString("rootPath", rootPath); + + this.application = application; + this.rootPath = rootPath; + } + + @Override + public int hashCode() + { + return (application.getName().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.getName().equals(that.application.getName()) && + this.rootPath.equals(that.rootPath); + } + else + { + return false; + } + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(512); + sb.append("AuditSession") + .append("[ application=").append(application.getName()) + .append(", rootPath=").append(rootPath) + .append("]"); + return sb.toString(); + } + + public Application getApplication() + { + return application; + } + + public String getRootPath() + { + return rootPath; + } +} diff --git a/source/java/org/alfresco/repo/audit/model/AuditModelReader.java b/source/java/org/alfresco/repo/audit/model/AuditModelReader.java index fd3dfdb09f..7b78d54e88 100644 --- a/source/java/org/alfresco/repo/audit/model/AuditModelReader.java +++ b/source/java/org/alfresco/repo/audit/model/AuditModelReader.java @@ -24,40 +24,32 @@ */ package org.alfresco.repo.audit.model; -import java.io.File; import java.net.URL; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.UnmarshalException; -import javax.xml.bind.Unmarshaller; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.audit.model._3.Audit; import org.alfresco.util.PropertyCheck; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.ResourceUtils; /** - * A component used to load Audit configuration XML documents. + * A component used to load Audit model XML documents. * * @author Derek Hulley * @since 3.2 */ public class AuditModelReader implements InitializingBean { - private URL configUrl; + private URL auditModelUrl; private AuditModelRegistry auditModelRegistry; /** * Set the XML location using file:, classpath: or any of the * {@link ResourceUtils Spring-supported} formats. * - * @param configUrl the location of the XML file + * @param auditModelUrl the location of the XML file */ - public void setConfigUrl(URL configUrl) + public void setAuditModelUrl(URL auditModelUrl) { - this.configUrl = configUrl; + this.auditModelUrl = auditModelUrl; } /** @@ -74,32 +66,9 @@ public class AuditModelReader implements InitializingBean */ public void afterPropertiesSet() throws Exception { - PropertyCheck.mandatory(this, "configUrl", configUrl); + PropertyCheck.mandatory(this, "configUrl", auditModelUrl); PropertyCheck.mandatory(this, "auditModelRegistry", auditModelRegistry); - File file = new File(configUrl.getFile()); - if (!file.exists()) - { - throw new AlfrescoRuntimeException("The Audit configuration XML was not found: " + configUrl); - } - - // Load it - JAXBContext jaxbCtx = JAXBContext.newInstance("org.alfresco.repo.audit.model._3"); - Unmarshaller jaxbUnmarshaller = jaxbCtx.createUnmarshaller(); - try - { - @SuppressWarnings("unchecked") - JAXBElement auditElement = (JAXBElement) jaxbUnmarshaller.unmarshal(configUrl); - Audit audit = auditElement.getValue(); - // Now register it - auditModelRegistry.registerModel(configUrl, audit); - } - catch (UnmarshalException e) - { - throw new AlfrescoRuntimeException( - "Failed to read Audit configuration XML: \n" + - " URL: " + configUrl + "\n" + - " Error: " + e.getMessage()); - } + auditModelRegistry.registerModel(auditModelUrl); } } diff --git a/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java b/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java index 565e808c63..65b30252ab 100644 --- a/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java +++ b/source/java/org/alfresco/repo/audit/model/AuditModelRegistry.java @@ -24,17 +24,33 @@ */ 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; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.Unmarshaller; + +import org.alfresco.error.AlfrescoRuntimeException; 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.domain.audit.AuditDAO; +import org.alfresco.service.cmr.repository.NodeRef; /** * Component used to store audit model definitions. It ensures that duplicate application and converter @@ -45,26 +61,191 @@ import org.alfresco.repo.audit.model._3.DataGenerators; */ public class AuditModelRegistry { - private final Map auditModelsByUrl; + private AuditDAO auditDAO; + + private final ReentrantReadWriteLock.ReadLock readLock; + private final ReentrantReadWriteLock.WriteLock writeLock; + + private final Set auditModelUrls; + private final List auditModels; private final Map dataExtractorsByName; private final Map dataGeneratorsByName; + /** + * Used to lookup the audit application java hierarchy + */ private final Map auditApplicationsByName; + /** + * Used to lookup a reference to the persisted config binary for an application + */ + private final Map auditModelIdsByApplicationsName; + /** + * Default constructor + */ public AuditModelRegistry() { - auditModelsByUrl = new HashMap(7); + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + readLock = lock.readLock(); + writeLock = lock.writeLock(); + + auditModelUrls = new HashSet(7); + auditModels = new ArrayList(7); dataExtractorsByName = new HashMap(13); dataGeneratorsByName = new HashMap(13); auditApplicationsByName = new HashMap(7); + auditModelIdsByApplicationsName = new HashMap(7); + } + + /** + * Set the DAO used to persisted the registered audit models + */ + public void setAuditDAO(AuditDAO auditDAO) + { + this.auditDAO = auditDAO; + } + + /** + * Register an audit model at a given URL. + * + * @param auditModelUrl the source of the model + */ + public void registerModel(URL auditModelUrl) + { + writeLock.lock(); + try + { + if (auditModelUrls.contains(auditModelUrl)) + { + throw new AlfrescoRuntimeException( + "An audit model has already been registered at URL " + auditModelUrl); + } + auditModelUrls.add(auditModelUrl); + } + finally + { + writeLock.unlock(); + } } /** - * Register an audit model. + * Register an audit model at a given node reference. * - * @param configurationUrl the source of the configuration - * @param audit the unmarshalled instance tree + * @param auditModelNodeRef the source of the audit model */ - public void registerModel(URL configurationUrl, Audit audit) + public void registerModel(NodeRef auditModelNodeRef) + { + writeLock.lock(); + try + { + throw new UnsupportedOperationException(); + } + finally + { + writeLock.unlock(); + } + } + + /** + * 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. + */ + public void loadAuditModels() + { + writeLock.lock(); + 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 + } + finally + { + writeLock.unlock(); + } + } + + /** + * Get the ID of the persisted audit model for the given application name + * + * @param application the name of the audited application + * @return the unique ID of the persisted model (null if not found) + */ + public Long getAuditModelId(String application) + { + readLock.lock(); + try + { + return auditModelIdsByApplicationsName.get(application); + } + finally + { + readLock.unlock(); + } + } + + /** + * Unmarshalls the Audit model from the URL. + * + * @throws AlfrescoRuntimeException if an IOException occurs + */ + public static Audit unmarshallModel(URL configUrl) + { + 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)); + return unmarshallModel(is, configUrl.toString()); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("The Audit model XML failed to load: " + configUrl, e); + } + } + + /** + * Unmarshalls the Audit model from a stream + */ + private static Audit unmarshallModel(InputStream is, String source) + { + try + { + JAXBContext jaxbCtx = JAXBContext.newInstance("org.alfresco.repo.audit.model._3"); + Unmarshaller jaxbUnmarshaller = jaxbCtx.createUnmarshaller(); + @SuppressWarnings("unchecked") + JAXBElement auditElement = (JAXBElement) jaxbUnmarshaller.unmarshal(is); + Audit audit = auditElement.getValue(); + // Done + return audit; + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException( + "Failed to read Audit model XML: \n" + + " Source: " + source + "\n" + + " Error: " + e.getMessage(), + e); + } + finally + { + try { is.close(); } catch (IOException e) {} + } + } + + private void cacheAuditElements(Long auditModelId, Audit audit) { // Get the data extractors and check for duplicates DataExtractors extractorsElement = audit.getDataExtractors(); @@ -134,8 +315,9 @@ public class AuditModelRegistry throw new AuditModelException("Audit application '" + name + "' has already been defined."); } auditApplicationsByName.put(name, application); + auditModelIdsByApplicationsName.put(name, auditModelId); } - // Store the model - auditModelsByUrl.put(configurationUrl, audit); + // Store the model itself + auditModels.add(audit); } } diff --git a/source/java/org/alfresco/repo/domain/audit/AbstractAuditDAOImpl.java b/source/java/org/alfresco/repo/domain/audit/AbstractAuditDAOImpl.java index fb4b9e2f7b..e4831ce976 100644 --- a/source/java/org/alfresco/repo/domain/audit/AbstractAuditDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/audit/AbstractAuditDAOImpl.java @@ -32,6 +32,7 @@ import java.util.zip.CRC32; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.domain.contentdata.ContentDataDAO; +import org.alfresco.repo.domain.propval.PropertyValueDAO; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; @@ -51,6 +52,7 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO private ContentService contentService; private ContentDataDAO contentDataDAO; + private PropertyValueDAO propertyValueDAO; public void setContentService(ContentService contentService) { @@ -61,11 +63,21 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO { this.contentDataDAO = contentDataDAO; } + + public void setPropertyValueDAO(PropertyValueDAO propertyValueDAO) + { + this.propertyValueDAO = propertyValueDAO; + } + + + /* + * alf_audit_model + */ /** * {@inheritDoc} */ - public Pair getOrCreateAuditConfig(URL url) + public Pair getOrCreateAuditModel(URL url) { InputStream is = null; try @@ -87,7 +99,7 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO while (true); long crc = crcCalc.getValue(); // Find an existing entry - AuditConfigEntity existingEntity = getAuditConfigByCrc(crc); + AuditModelEntity existingEntity = getAuditModelByCrc(crc); if (existingEntity != null) { Long existingEntityId = existingEntity.getId(); @@ -100,7 +112,7 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO if (logger.isDebugEnabled()) { logger.debug( - "Found existing configuration with same CRC: \n" + + "Found existing model with same CRC: \n" + " URL: " + url + "\n" + " CRC: " + crc + "\n" + " Result: " + result); @@ -118,13 +130,13 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO writer.putContent(is); ContentData newContentData = writer.getContentData(); Long newContentDataId = contentDataDAO.createContentData(newContentData).getFirst(); - AuditConfigEntity newEntity = createAuditConfig(newContentDataId, crc); + AuditModelEntity newEntity = createAuditModel(newContentDataId, crc); Pair result = new Pair(newEntity.getId(), newContentData); // Done if (logger.isDebugEnabled()) { logger.debug( - "Created new audit config: \n" + + "Created new audit model: \n" + " URL: " + url + "\n" + " CRC: " + crc + "\n" + " Result: " + result); @@ -134,7 +146,7 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO } catch (IOException e) { - throw new AlfrescoRuntimeException("Failed to read Audit configuration: " + url); + throw new AlfrescoRuntimeException("Failed to read Audit model: " + url); } finally { @@ -145,6 +157,22 @@ public abstract class AbstractAuditDAOImpl implements AuditDAO } } - protected abstract AuditConfigEntity getAuditConfigByCrc(long crc); - protected abstract AuditConfigEntity createAuditConfig(Long contentDataId, long crc); + protected abstract AuditModelEntity getAuditModelByCrc(long crc); + protected abstract AuditModelEntity createAuditModel(Long contentDataId, long crc); + + /* + * alf_audit_model + */ + + public Long createAuditSession(Long modelId, String application) + { + // Persist the string + Long appNameId = propertyValueDAO.getOrCreatePropertyValue(application).getFirst(); + // Create the audit session + AuditSessionEntity entity = createAuditSession(appNameId, modelId); + // Done + return entity.getId(); + } + + protected abstract AuditSessionEntity createAuditSession(Long appNameId, Long modelId); } diff --git a/source/java/org/alfresco/repo/domain/audit/AuditDAO.java b/source/java/org/alfresco/repo/domain/audit/AuditDAO.java index 9bfd0bf020..87bea74e71 100644 --- a/source/java/org/alfresco/repo/domain/audit/AuditDAO.java +++ b/source/java/org/alfresco/repo/domain/audit/AuditDAO.java @@ -38,11 +38,13 @@ import org.alfresco.util.Pair; public interface AuditDAO { /** - * Creates a new audit config entry or finds an existing one + * Creates a new audit model entry or finds an existing one * * @param the URL of the configuration * @return Returns the ID of the config matching the input stream and the * content storage details */ - Pair getOrCreateAuditConfig(URL url); + Pair getOrCreateAuditModel(URL url); + + Long createAuditSession(Long modelId, String application); } diff --git a/source/java/org/alfresco/repo/domain/audit/AuditDAOTest.java b/source/java/org/alfresco/repo/domain/audit/AuditDAOTest.java index b6c3556dda..beb351aa7b 100644 --- a/source/java/org/alfresco/repo/domain/audit/AuditDAOTest.java +++ b/source/java/org/alfresco/repo/domain/audit/AuditDAOTest.java @@ -1,87 +1,122 @@ -/* - * 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.domain.audit; - -import java.io.File; -import java.net.URL; - -import junit.framework.TestCase; - -import org.alfresco.repo.content.transform.AbstractContentTransformerTest; -import org.alfresco.repo.domain.contentdata.ContentDataDAO; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.repository.ContentData; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.ApplicationContextHelper; -import org.alfresco.util.Pair; -import org.springframework.context.ConfigurableApplicationContext; - -/** - * @see ContentDataDAO - * - * @author Derek Hulley - * @since 3.2 - */ -public class AuditDAOTest extends TestCase -{ - private ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) ApplicationContextHelper.getApplicationContext(); - - private TransactionService transactionService; - private RetryingTransactionHelper txnHelper; - private AuditDAO auditDAO; - - @Override - public void setUp() throws Exception - { - ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); - transactionService = serviceRegistry.getTransactionService(); - txnHelper = transactionService.getRetryingTransactionHelper(); - - auditDAO = (AuditDAO) ctx.getBean("auditDAO"); - } - - public void testAuditConfig() throws Exception - { - final File file = AbstractContentTransformerTest.loadQuickTestFile("pdf"); - assertNotNull(file); - final URL url = new URL("file:" + file.getAbsolutePath()); - RetryingTransactionCallback> callback = new RetryingTransactionCallback>() - { - public Pair execute() throws Throwable - { - Pair contentDataPair = auditDAO.getOrCreateAuditConfig(url); - return contentDataPair; - } - }; - Pair configPair = txnHelper.doInTransaction(callback); - assertNotNull(configPair); - // Now repeat. The results should be exactly the same. - Pair configPairCheck = txnHelper.doInTransaction(callback); - assertNotNull(configPairCheck); - assertEquals(configPair, configPairCheck); - } -} +/* + * 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.domain.audit; + +import java.io.File; +import java.net.URL; + +import junit.framework.TestCase; + +import org.alfresco.repo.content.transform.AbstractContentTransformerTest; +import org.alfresco.repo.domain.contentdata.ContentDataDAO; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.Pair; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * @see ContentDataDAO + * + * @author Derek Hulley + * @since 3.2 + */ +public class AuditDAOTest extends TestCase +{ + private ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) ApplicationContextHelper.getApplicationContext(); + + private TransactionService transactionService; + private RetryingTransactionHelper txnHelper; + private AuditDAO auditDAO; + + @Override + public void setUp() throws Exception + { + ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + transactionService = serviceRegistry.getTransactionService(); + txnHelper = transactionService.getRetryingTransactionHelper(); + + auditDAO = (AuditDAO) ctx.getBean("auditDAO"); + } + + public void testAuditModel() throws Exception + { + final File file = AbstractContentTransformerTest.loadQuickTestFile("pdf"); + assertNotNull(file); + final URL url = new URL("file:" + file.getAbsolutePath()); + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() + { + public Pair execute() throws Throwable + { + Pair auditModelPair = auditDAO.getOrCreateAuditModel(url); + return auditModelPair; + } + }; + Pair configPair = txnHelper.doInTransaction(callback); + assertNotNull(configPair); + // Now repeat. The results should be exactly the same. + Pair configPairCheck = txnHelper.doInTransaction(callback); + assertNotNull(configPairCheck); + assertEquals(configPair, configPairCheck); + } + + public void testAuditSession() throws Exception + { + final File file = AbstractContentTransformerTest.loadQuickTestFile("pdf"); + assertNotNull(file); + final URL url = new URL("file:" + file.getAbsolutePath()); + RetryingTransactionCallback createModelCallback = new RetryingTransactionCallback() + { + public Long execute() throws Throwable + { + return auditDAO.getOrCreateAuditModel(url).getFirst(); + } + }; + final Long modelId = txnHelper.doInTransaction(createModelCallback); + + final String appName = getName() + "." + System.currentTimeMillis(); + final int count = 1000; + RetryingTransactionCallback createSessionCallback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + for (int i = 0; i < count; i++) + { + auditDAO.createAuditSession(modelId, appName); + } + return null; + } + }; + long before = System.nanoTime(); + txnHelper.doInTransaction(createSessionCallback); + long after = System.nanoTime(); + System.out.println( + "Time for " + count + " session creations was " + + ((double)(after - before)/(10E6)) + "ms"); + } +} diff --git a/source/java/org/alfresco/repo/domain/audit/AuditConfigEntity.java b/source/java/org/alfresco/repo/domain/audit/AuditModelEntity.java similarity index 87% rename from source/java/org/alfresco/repo/domain/audit/AuditConfigEntity.java rename to source/java/org/alfresco/repo/domain/audit/AuditModelEntity.java index 0df2a6dad0..19806180ea 100644 --- a/source/java/org/alfresco/repo/domain/audit/AuditConfigEntity.java +++ b/source/java/org/alfresco/repo/domain/audit/AuditModelEntity.java @@ -1,110 +1,110 @@ -/* - * 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.domain.audit; - -import org.alfresco.util.EqualsHelper; - -/** - * Entity bean for alf_audit_cfg table. - * - * @author Derek Hulley - * @since 3.2 - */ -public class AuditConfigEntity -{ - private Long id; - private Long contentDataId; - private long contentCrc; - - public AuditConfigEntity() - { - } - - @Override - public int hashCode() - { - return (int) contentCrc; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj instanceof AuditConfigEntity) - { - AuditConfigEntity that = (AuditConfigEntity) obj; - return EqualsHelper.nullSafeEquals(this.id, that.id); - } - else - { - return false; - } - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(512); - sb.append("AuditConfigEntity") - .append("[ ID=").append(id) - .append(", contentDataId=").append(contentDataId) - .append(", contentCrc=").append(contentCrc) - .append("]"); - return sb.toString(); - } - - public Long getId() - { - return id; - } - - public void setId(Long id) - { - this.id = id; - } - - public Long getContentDataId() - { - return contentDataId; - } - - public void setContentDataId(Long contentDataId) - { - this.contentDataId = contentDataId; - } - - public long getContentCrc() - { - return contentCrc; - } - - public void setContentCrc(long contentCrc) - { - this.contentCrc = contentCrc; - } -} +/* + * 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.domain.audit; + +import org.alfresco.util.EqualsHelper; + +/** + * Entity bean for alf_audit_model table. + * + * @author Derek Hulley + * @since 3.2 + */ +public class AuditModelEntity +{ + private Long id; + private Long contentDataId; + private long contentCrc; + + public AuditModelEntity() + { + } + + @Override + public int hashCode() + { + return (int) contentCrc; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj instanceof AuditModelEntity) + { + AuditModelEntity that = (AuditModelEntity) obj; + return EqualsHelper.nullSafeEquals(this.id, that.id); + } + else + { + return false; + } + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(512); + sb.append("AuditModelEntity") + .append("[ ID=").append(id) + .append(", contentDataId=").append(contentDataId) + .append(", contentCrc=").append(contentCrc) + .append("]"); + return sb.toString(); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public Long getContentDataId() + { + return contentDataId; + } + + public void setContentDataId(Long contentDataId) + { + this.contentDataId = contentDataId; + } + + public long getContentCrc() + { + return contentCrc; + } + + public void setContentCrc(long contentCrc) + { + this.contentCrc = contentCrc; + } +} diff --git a/source/java/org/alfresco/repo/domain/audit/AuditSessionEntity.java b/source/java/org/alfresco/repo/domain/audit/AuditSessionEntity.java new file mode 100644 index 0000000000..3036891da3 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/audit/AuditSessionEntity.java @@ -0,0 +1,84 @@ +/* + * 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.domain.audit; + +/** + * Entity bean for alf_audit_session table. + * + * @author Derek Hulley + * @since 3.2 + */ +public class AuditSessionEntity +{ + private Long id; + private Long applicationNameId; + private Long auditModelId; + + public AuditSessionEntity() + { + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(512); + sb.append("AuditSessionEntity") + .append("[ ID=").append(id) + .append(", applicationNameId=").append(applicationNameId) + .append(", auditModelId=").append(auditModelId) + .append("]"); + return sb.toString(); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public Long getAuditModelId() + { + return auditModelId; + } + + public void setAuditModelId(Long auditModelId) + { + this.auditModelId = auditModelId; + } + + public Long getApplicationNameId() + { + return applicationNameId; + } + + public void setApplicationNameId(Long applicationNameId) + { + this.applicationNameId = applicationNameId; + } +} 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 da147c2130..a420bf9bb8 100644 --- a/source/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/audit/ibatis/AuditDAOImpl.java @@ -25,7 +25,8 @@ package org.alfresco.repo.domain.audit.ibatis; import org.alfresco.repo.domain.audit.AbstractAuditDAOImpl; -import org.alfresco.repo.domain.audit.AuditConfigEntity; +import org.alfresco.repo.domain.audit.AuditModelEntity; +import org.alfresco.repo.domain.audit.AuditSessionEntity; import org.springframework.orm.ibatis.SqlMapClientTemplate; /** @@ -36,8 +37,10 @@ import org.springframework.orm.ibatis.SqlMapClientTemplate; */ public class AuditDAOImpl extends AbstractAuditDAOImpl { - private static final String SELECT_CONFIG_BY_CRC = "select.AuditConfigByCrc"; - private static final String INSERT_CONFIG = "insert.AuditConfig"; + 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 SqlMapClientTemplate template; @@ -47,24 +50,35 @@ public class AuditDAOImpl extends AbstractAuditDAOImpl } @Override - protected AuditConfigEntity getAuditConfigByCrc(long crc) + protected AuditModelEntity getAuditModelByCrc(long crc) { - AuditConfigEntity entity = new AuditConfigEntity(); + AuditModelEntity entity = new AuditModelEntity(); entity.setContentCrc(crc); - entity = (AuditConfigEntity) template.queryForObject( - SELECT_CONFIG_BY_CRC, + entity = (AuditModelEntity) template.queryForObject( + SELECT_MODEL_BY_CRC, entity); // Done return entity; } @Override - protected AuditConfigEntity createAuditConfig(Long contentDataId, long crc) + protected AuditModelEntity createAuditModel(Long contentDataId, long crc) { - AuditConfigEntity entity = new AuditConfigEntity(); + AuditModelEntity entity = new AuditModelEntity(); entity.setContentDataId(contentDataId); entity.setContentCrc(crc); - Long id = (Long) template.insert(INSERT_CONFIG, entity); + Long id = (Long) template.insert(INSERT_MODEL, entity); + entity.setId(id); + return entity; + } + + @Override + protected AuditSessionEntity createAuditSession(Long appNameId, Long modelId) + { + AuditSessionEntity entity = new AuditSessionEntity(); + entity.setApplicationNameId(appNameId); + entity.setAuditModelId(modelId); + Long id = (Long) template.insert(INSERT_SESSION, entity); entity.setId(id); return entity; }