diff --git a/config/alfresco/templates/content/examples/doc_info.ftl b/config/alfresco/templates/content/examples/doc_info.ftl
index 00af7cc5b0..2821edeeee 100644
--- a/config/alfresco/templates/content/examples/doc_info.ftl
+++ b/config/alfresco/templates/content/examples/doc_info.ftl
@@ -1,18 +1,47 @@
<#-- Shows some general info about the current document, including NodeRef and aspects applied -->
-
+ <#-- Get a list of all the property names for the document -->
+ <#assign props = document.properties?keys>
+ <#list props as t>
+ <#-- If the property exists -->
+ <#if document.properties[t]?exists>
+ <#-- If it is a date, format it accordingly -->
+ <#if document.properties[t]?is_date>
+
${t} = ${document.properties[t]?datetime}
+
+ <#-- If it is a boolean, format it accordingly -->
+ <#elseif document.properties[t]?is_boolean>
+
diff --git a/config/alfresco/templates/content/examples/my_pressreleases.ftl b/config/alfresco/templates/content/examples/my_pressreleases.ftl
deleted file mode 100644
index 1fa8d9c825..0000000000
--- a/config/alfresco/templates/content/examples/my_pressreleases.ftl
+++ /dev/null
@@ -1,22 +0,0 @@
-<#-- Displays a table of all the documents from a "Press Releases" folder under Company Home -->
-<#-- Obviously this folder needs to exist and the docs in it should have the title and description fields set -->
-
diff --git a/config/alfresco/templates/content/examples/userhome_docs.ftl b/config/alfresco/templates/content/examples/userhome_docs.ftl
deleted file mode 100644
index 180fb92aa7..0000000000
--- a/config/alfresco/templates/content/examples/userhome_docs.ftl
+++ /dev/null
@@ -1,15 +0,0 @@
-<#-- List of docs in the Home Space for current user -->
-<#-- If the doc mimetype is plain/text then the content is shown inline -->
-<#-- If the doc mimetype is JPEG then the image is shown inline as a small thumbnail image -->
-
-<#list userhome.children as child>
- <#if child.isDocument>
-
${child.properties.name}
- <#if child.mimetype = "text/plain">
-
${child.content}
- <#elseif child.mimetype = "image/jpeg">
-
- #if>
- #if>
-#list>
-
diff --git a/config/alfresco/templates/content/examples/xpath_search.ftl b/config/alfresco/templates/content/examples/xpath_search.ftl
deleted file mode 100644
index b81f9440d5..0000000000
--- a/config/alfresco/templates/content/examples/xpath_search.ftl
+++ /dev/null
@@ -1,26 +0,0 @@
-<#-- Shows use of the childByNamePath, childrenByXPath and childrenByLuceneSearch API -->
-
-
Template Documents in 'Company Home/Data Dictionary/Presentation Templates':
-
-<#list companyhome.childByNamePath["Data Dictionary/Presentation Templates"].children as child>
- <#if child.isDocument>
-
diff --git a/config/alfresco/templates/content_template_examples.xml b/config/alfresco/templates/content_template_examples.xml
index f993d5a5fe..e0ae7574bc 100644
--- a/config/alfresco/templates/content_template_examples.xml
+++ b/config/alfresco/templates/content_template_examples.xml
@@ -7,8 +7,8 @@
true
- Displays basic information about the current document
- contentUrl=classpath:alfresco/templates/content/examples/doc_info.ftl|mimetype=text/plain|size=636|encoding=UTF-8
+ Displays useful information about the current document
+ contentUrl=classpath:alfresco/templates/content/examples/doc_info.ftl|mimetype=text/plain|size=1884|encoding=UTF-8doc_info.ftldoc_info.ftl
@@ -84,20 +84,6 @@
-
-
-
-
-
-
- true
- Renders a valid RSS2.0 XML document showing the documents in the current space created or modified in the last 7 days. The template should be configured to use the appropriate server and port before use.
- contentUrl=classpath:alfresco/templates/content/examples/RSS_2.0_recent_docs.ftl|mimetype=text/plain|size=1917|encoding=UTF-8
- RSS_2.0_recent_docs.ftl
- RSS_2.0_recent_docs.ftl
-
-
-
@@ -140,18 +126,4 @@
-
-
-
-
-
-
- true
- Example of using XPath and Lucene searches within a template.
- contentUrl=classpath:alfresco/templates/content/examples/xpath_search.ftl|mimetype=text/plain|size=1109|encoding=UTF-8
- xpath_search.ftl
- xpath_search.ftl
-
-
-
\ No newline at end of file
diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties
index 4daccb2687..87530803c5 100644
--- a/config/alfresco/version.properties
+++ b/config/alfresco/version.properties
@@ -7,7 +7,7 @@
version.major=1
version.minor=4
version.revision=0
-version.label=Dev
+version.label=Preview
# Edition label
@@ -19,4 +19,4 @@ version.build=@build-number@
# Schema number
-version.schema=20
+version.schema=21
diff --git a/source/java/log4j.properties b/source/java/log4j.properties
index af5a9b069d..45d5653b5c 100644
--- a/source/java/log4j.properties
+++ b/source/java/log4j.properties
@@ -6,6 +6,6 @@ log4j.appender.stdout.layout.ConversionPattern=%d %5p %c{1}:%m%n
### Set log levels.
log4j.rootLogger=warn, stdout
-log4j.logger.org.hibernate=fatal
+log4j.logger.org.hibernate=error
log4j.logger.org.alfresco.repo.avm=info
log4j.logger.org.springframework=warn
\ No newline at end of file
diff --git a/source/java/org/alfresco/model/ContentModel.java b/source/java/org/alfresco/model/ContentModel.java
index 4c53e07d22..72ec93e28b 100644
--- a/source/java/org/alfresco/model/ContentModel.java
+++ b/source/java/org/alfresco/model/ContentModel.java
@@ -77,6 +77,7 @@ public interface ContentModel
static final QName PROP_SYS_VERSION_SCHEMA = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionSchema");
static final QName PROP_SYS_VERSION_EDITION = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionEdition");
+
//
// Content Model Definitions
//
@@ -189,6 +190,12 @@ public interface ContentModel
public static final QName ASPECT_MOUNTED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mounted");
public static final QName PROP_MOUNTPOINT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mountpoint");
+ // countable aspect
+ public static final QName ASPECT_COUNTABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "countable");
+ public static final QName PROP_HITS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "hits");
+ public static final QName PROP_COUNTER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "counter");
+
+
//
// Application Model Definitions
//
diff --git a/source/java/org/alfresco/repo/action/executer/CounterIncrementActionExecuter.java b/source/java/org/alfresco/repo/action/executer/CounterIncrementActionExecuter.java
new file mode 100644
index 0000000000..f779787433
--- /dev/null
+++ b/source/java/org/alfresco/repo/action/executer/CounterIncrementActionExecuter.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.action.executer;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.service.cmr.action.Action;
+import org.alfresco.service.cmr.action.ParameterDefinition;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * Simple action to increment an integer value. The runtime NodeService is used so any user
+ * can increment the counter value on a node.
+ *
+ * @author Kevin Roast
+ */
+public class CounterIncrementActionExecuter extends ActionExecuterAbstractBase
+{
+ /** Runtime NodeService with no permissions protection */
+ private NodeService nodeService;
+
+
+ /**
+ * @param nodeService The Runtime NodeService to set.
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef)
+ */
+ protected void executeImpl(Action action, NodeRef actionedUponNodeRef)
+ {
+ // add the cm:countable aspect as required
+ if (this.nodeService.hasAspect(actionedUponNodeRef, ContentModel.ASPECT_COUNTABLE) == false)
+ {
+ // set the value to 1 by default
+ Map props = new HashMap(1);
+ props.put(ContentModel.PROP_COUNTER, 1);
+ this.nodeService.addAspect(actionedUponNodeRef, ContentModel.ASPECT_COUNTABLE, props);
+ }
+ else
+ {
+ // increment the value and handle possibility that no value has been set yet
+ int resultValue = 1;
+ Integer value = (Integer)this.nodeService.getProperty(actionedUponNodeRef, ContentModel.PROP_COUNTER);
+ if (value != null)
+ {
+ resultValue = value.intValue() + 1;
+ }
+ this.nodeService.setProperty(actionedUponNodeRef, ContentModel.PROP_COUNTER, resultValue);
+ }
+ }
+
+ /**
+ * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List)
+ */
+ protected void addParameterDefinitions(List paramList)
+ {
+ // none required
+ }
+}
diff --git a/source/java/org/alfresco/repo/action/executer/ScriptActionExecutor.java b/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java
similarity index 94%
rename from source/java/org/alfresco/repo/action/executer/ScriptActionExecutor.java
rename to source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java
index 1815acecd3..2a49156dee 100644
--- a/source/java/org/alfresco/repo/action/executer/ScriptActionExecutor.java
+++ b/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java
@@ -32,9 +32,13 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.PersonService;
/**
+ * Action to execute a JavaScript. The script has access to the default model.
+ * The actionedUponNodeRef is added to the default model as the 'document' and the owning
+ * NodeRef is added as the 'space'.
+ *
* @author Kevin Roast
*/
-public class ScriptActionExecutor extends ActionExecuterAbstractBase
+public class ScriptActionExecuter extends ActionExecuterAbstractBase
{
public static final String NAME = "script";
public static final String PARAM_SCRIPTREF = "script-ref";
diff --git a/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java b/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java
index 12247e4b77..d0f2655d69 100644
--- a/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java
+++ b/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java
@@ -60,7 +60,7 @@ public class PatchExecuter implements ApplicationListener
{
logger.info(I18NUtil.getMessage(MSG_CHECKING));
- Date before = new Date(System.currentTimeMillis() - 20000L); // 20 seconds ago
+ Date before = new Date(System.currentTimeMillis() - 60000L); // 60 seconds ago
patchService.applyOutstandingPatches();
Date after = new Date(System .currentTimeMillis() + 20000L); // 20 seconds ahead
diff --git a/source/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java
index 69dfd1e092..a68dc08fec 100644
--- a/source/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java
+++ b/source/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java
@@ -20,7 +20,7 @@ import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.service.cmr.admin.PatchException;
/**
- * This patch ensures that an upgrade script has been executed. Upgrade scripts
+ * This patch ensures that an upgrade scriptUrl has been executed. Upgrade scripts
* should create an entry for the patch with the required ID and execution status
* so that the code in this class is never called. If called, an exception message
* is always generated.
@@ -31,26 +31,37 @@ public class SchemaUpgradeScriptPatch extends AbstractPatch
{
private static final String MSG_NOT_EXECUTED = "patch.schemaUpgradeScript.err.not_executed";
- private String scriptName;
+ private String scriptUrl;
public SchemaUpgradeScriptPatch()
{
}
+
+ /**
+ * @return Returns the URL of the scriptUrl that has to have been run
+ */
+ public String getScriptUrl()
+ {
+ return scriptUrl;
+ }
/**
- * Set the name of the upgrade script to execute.
+ * Set the URL of the upgrade scriptUrl to execute. This is the full URL of the
+ * file, e.g. classpath:alfresco/patch/scripts/upgrade-1.4/${hibernate.dialect.class}/patchAlfrescoSchemaUpdate-1.4-2.sql
+ * where the ${hibernate.dialect.class} placeholder will be substituted with the Hibernate
+ * Dialect as configured for the system.
*
- * @param scriptName the script filename
+ * @param scriptUrl the scriptUrl filename
*/
- public void setScriptName(String scriptName)
+ public void setScriptUrl(String script)
{
- this.scriptName = scriptName;
+ this.scriptUrl = script;
}
protected void checkProperties()
{
super.checkProperties();
- checkPropertyNotNull(scriptName, "scriptName");
+ checkPropertyNotNull(scriptUrl, "scriptUrl");
}
/**
@@ -59,6 +70,6 @@ public class SchemaUpgradeScriptPatch extends AbstractPatch
@Override
protected String applyInternal() throws Exception
{
- throw new PatchException(MSG_NOT_EXECUTED, scriptName);
+ throw new PatchException(MSG_NOT_EXECUTED, scriptUrl);
}
}
diff --git a/source/java/org/alfresco/repo/admin/patch/impl/UniqueChildNamePatch.java b/source/java/org/alfresco/repo/admin/patch/impl/UniqueChildNamePatch.java
index d2525361cd..28a77aadd3 100644
--- a/source/java/org/alfresco/repo/admin/patch/impl/UniqueChildNamePatch.java
+++ b/source/java/org/alfresco/repo/admin/patch/impl/UniqueChildNamePatch.java
@@ -216,6 +216,9 @@ public class UniqueChildNamePatch extends AbstractPatch
writeLine(" Replaced with: " + usedChildName);
}
}
+ // clear the session to preserve memory
+ getSession().flush();
+ getSession().clear();
}
}
diff --git a/source/java/org/alfresco/repo/audit/ApplicationAuditModel.java b/source/java/org/alfresco/repo/audit/ApplicationAuditModel.java
new file mode 100644
index 0000000000..496873c201
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/ApplicationAuditModel.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import org.alfresco.service.cmr.repository.NodeRef;
+
+public interface ApplicationAuditModel
+{
+ /**
+ * Report if audit behaviour can be determined before the method call
+ *
+ * @param auditState,
+ * @param mi
+ * @return
+ */
+ public AuditMode beforeExecution(AuditMode auditMode, String application, String description,
+ NodeRef key, Object... args);
+
+ /**
+ * Report if audit behaviour can be determined after the method call
+ *
+ * @param auditState,
+ * @param mi
+ * @return
+ */
+ public AuditMode afterExecution(AuditMode auditMode, String application, String description,
+ NodeRef key, Object... args);
+
+ /**
+ * Report if audit behaviour should be invoked on error. It could be we look at the error and filter - this is not supported at the moment.
+ *
+ * @param auditState,
+ * @param mi
+ * @return
+ */
+ public AuditMode onError(AuditMode auditMode, String application, String description,
+ NodeRef key, Object... args);
+
+ /**
+ * Get the optional parameters that are to be recorded
+ *
+ * @param mi
+ * @return
+ */
+ public RecordOptions getAuditRecordOptions(String application);
+}
diff --git a/source/java/org/alfresco/repo/audit/AuditComponent.java b/source/java/org/alfresco/repo/audit/AuditComponent.java
new file mode 100644
index 0000000000..3b13c7e5f5
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditComponent.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.aopalliance.intercept.MethodInvocation;
+
+/**
+ * The audit component.
+ *
+ * Used by the AuditService and AuditMethodInterceptor to insert audit entries.
+ *
+ * @author Andy Hind
+ */
+public interface AuditComponent
+{
+ /**
+ * Audit entry point for method interceptors.
+ *
+ * @param methodInvocation
+ */
+ public Object audit(MethodInvocation methodInvocation) throws Throwable;
+
+ /**
+ *
+ * @param source -
+ * a string that represents the application
+ * @param description -
+ * the audit entry *
+ * @param key -
+ * a node ref to use as the key for filtering etc
+ * @param args -
+ * an arbitrary list of parameters
+ */
+ public void audit(String source, String description, NodeRef key, Object... args);
+
+
+}
diff --git a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java
new file mode 100644
index 0000000000..e476907417
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Date;
+
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
+import org.alfresco.service.Auditable;
+import org.alfresco.service.NotAuditable;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * The default audit component implementation. TODO: Implement before, after and exception filtering. At the moment these filters are ignired. TODO: Respect audit internal - at the
+ * moment audit internal is fixed to false.
+ *
+ * @author Andy Hind
+ */
+public class AuditComponentImpl implements AuditComponent
+{
+ /**
+ * The application name to use for audit entries generated by method interception around public services.
+ */
+ private static final String SYSTEM_APPLICATION = "SystemMethodInterceptor";
+
+ /**
+ * Logging
+ */
+ private static Log s_logger = LogFactory.getLog(AuditComponentImpl.class);
+
+ /**
+ * Suspend resume auditing
+ */
+ private static ThreadLocal auditFlag = new ThreadLocal();
+
+ /**
+ * IOC
+ */
+ private PublicServiceIdentifier publicServiceIdentifier;
+
+ private AuditConfiguration auditConfiguration;
+
+ private AuditDAO auditDAO;
+
+ private AuditDAO auditFailedDAO;
+
+ private AuditModel auditModel;
+
+ /**
+ * Keep hold of the host where the audit occurs. TODO: Check that we get the correct address ...
+ */
+
+ private InetAddress auditHost;
+
+ public AuditComponentImpl()
+ {
+ super();
+ // Initialise the host address
+ try
+ {
+ auditHost = InetAddress.getLocalHost();
+ }
+ catch (UnknownHostException e)
+ {
+ s_logger.error("Failed to get local host address", e);
+ }
+ }
+
+ /*
+ * IOC property setters
+ */
+
+ public void setAuditDAO(AuditDAO auditDAO)
+ {
+ this.auditDAO = auditDAO;
+ }
+
+ public void setAuditFailedDAO(AuditDAO auditFailedDAO)
+ {
+ this.auditFailedDAO = auditFailedDAO;
+ }
+
+ public void setAuditConfiguration(AuditConfiguration auditConfiguration)
+ {
+ this.auditConfiguration = auditConfiguration;
+ }
+
+ public void setPublicServiceIdentifier(PublicServiceIdentifier publicServiceIdentifier)
+ {
+ this.publicServiceIdentifier = publicServiceIdentifier;
+ }
+
+ public void setAuditModel(AuditModel auditModel)
+ {
+ this.auditModel = auditModel;
+ }
+
+ public Object audit(MethodInvocation mi) throws Throwable
+ {
+ if ((auditFlag.get() == null) || (!auditFlag.get().booleanValue()))
+ {
+ try
+ {
+ auditFlag.set(Boolean.TRUE);
+
+ Method method = mi.getMethod();
+ String methodName = method.getName();
+ String serviceName = publicServiceIdentifier.getPublicServiceName(mi);
+ if (method.isAnnotationPresent(Auditable.class))
+ {
+
+ if (serviceName != null)
+ {
+ if (s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Auditing - " + serviceName + "." + methodName);
+ }
+ return auditImpl(mi);
+ }
+ else
+ {
+ if (s_logger.isDebugEnabled())
+ {
+ s_logger.debug("UnknownService." + methodName);
+ }
+ return auditImpl(mi);
+ }
+
+ }
+ else if (method.isAnnotationPresent(NotAuditable.class))
+ {
+ if (s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Not Audited. " + serviceName + "." + methodName);
+ }
+ return mi.proceed();
+ }
+ else
+ {
+ if (s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Unannotated service method " + serviceName + "." + methodName);
+ }
+ throw new RuntimeException("Unannotated service method " + serviceName + "." + methodName);
+ }
+ }
+ finally
+ {
+ auditFlag.set(Boolean.FALSE);
+ }
+ }
+ else
+ {
+ return mi.proceed();
+ }
+ }
+
+ /**
+ * Audit a method invocation
+ */
+ public Object auditImpl(MethodInvocation mi) throws Throwable
+ {
+ AuditInfo auditInfo = new AuditInfo(auditConfiguration);
+ // RecordOptions recordOptions = auditModel.getAuditRecordOptions(mi);
+ AuditMode auditMode = AuditMode.UNSET;
+ try
+ {
+ auditMode = beforeInvocation(auditMode, auditInfo, mi);
+ Object o = mi.proceed();
+ auditMode = postInvocation(auditMode, auditInfo, mi, o);
+ if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.SUCCESS))
+ {
+ auditDAO.audit(auditInfo);
+ }
+ return o;
+ }
+ catch (Throwable t)
+ {
+ auditMode = onError(auditMode, auditInfo, mi, t);
+ if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL))
+ {
+ try
+ {
+ auditFailedDAO.audit(auditInfo);
+ }
+ catch (Throwable tt)
+ {
+ throw new AuditException("Failed to audit exception", new Object[] { tt }, t);
+ }
+ }
+ throw t;
+ }
+ }
+
+ /**
+ * Helper method to set auditable properties and to determine if auditing is required when an exception is caught in the audited method.
+ *
+ * @param auditMode
+ * @param auditInfo
+ * @param t
+ * @return
+ */
+ private AuditMode onError(AuditMode auditMode, AuditInfo auditInfo, MethodInvocation mi, Throwable t)
+ {
+ if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL))
+ {
+ auditInfo.setFail(true);
+ auditInfo.setThrowable(t);
+ }
+
+ return auditMode;
+ }
+
+ /**
+ * Helper method to set audited information after method invocation and to determine if auditing should take place based on the method return value.
+ *
+ * @param auditMode
+ * @param auditInfo
+ * @param mi
+ * @param returnObject
+ * @return
+ */
+ private AuditMode postInvocation(AuditMode auditMode, AuditInfo auditInfo, MethodInvocation mi, Object returnObject)
+ {
+ if (returnObject == null)
+ {
+ auditInfo.setReturnObject(null);
+ }
+ else if (returnObject instanceof Serializable)
+ {
+ auditInfo.setReturnObject((Serializable) returnObject);
+ }
+ else
+ {
+ auditInfo.setReturnObject(returnObject.toString());
+ }
+ return auditMode;
+ }
+
+ /**
+ * Set auditable information and determine if auditing is required before method invocation. This would normally be based on the method arguments.
+ *
+ * @param auditMode
+ * @param auditInfo
+ * @param mi
+ * @return
+ */
+ private AuditMode beforeInvocation(AuditMode auditMode, AuditInfo auditInfo, MethodInvocation mi)
+ {
+ AuditMode effectiveAuditMode = auditModel.beforeExecution(auditMode, mi);
+
+ if (auditMode != AuditMode.NONE)
+ {
+ String methodName = mi.getMethod().getName();
+ String serviceName = publicServiceIdentifier.getPublicServiceName(mi);
+ auditInfo.setAuditApplication(SYSTEM_APPLICATION);
+ auditInfo.setAuditConfiguration(auditConfiguration);
+ auditInfo.setAuditMethod(methodName);
+ auditInfo.setAuditService(serviceName);
+ auditInfo.setClientAddress(null);
+ auditInfo.setDate(new Date());
+ auditInfo.setFail(false);
+ auditInfo.setFiltered(false);
+ auditInfo.setHostAddress(auditHost);
+ auditInfo.setKeyGUID(null);
+ auditInfo.setKeyPropertiesAfter(null);
+ auditInfo.setKeyPropertiesBefore(null);
+ auditInfo.setKeyStore(null);
+ auditInfo.setMessage(null);
+ if (mi.getArguments() != null)
+ {
+ Serializable[] serArgs = new Serializable[mi.getArguments().length];
+ for (int i = 0; i < mi.getArguments().length; i++)
+ {
+ if (mi.getArguments()[i] == null)
+ {
+ serArgs[i] = null;
+ }
+ else if (mi.getArguments()[i] instanceof Serializable)
+ {
+ serArgs[i] = (Serializable) mi.getArguments()[i];
+ }
+ else
+ {
+ serArgs[i] = mi.getArguments()[i].toString();
+ }
+ }
+ auditInfo.setMethodArguments(serArgs);
+ }
+ auditInfo.setPath(null);
+ auditInfo.setReturnObject(null);
+ auditInfo.setSessionId(null);
+ auditInfo.setThrowable(null);
+ auditInfo.setTxId(AlfrescoTransactionSupport.getTransactionId());
+ auditInfo.setUserIdentifier(AuthenticationUtil.getCurrentUserName());
+ }
+
+ return effectiveAuditMode;
+ }
+
+ /**
+ * A simple audit entry Currently we ignore filtering here.
+ */
+ public void audit(String source, String description, NodeRef key, Object... args)
+ {
+ AuditInfo auditInfo = new AuditInfo(auditConfiguration);
+ // RecordOptions recordOptions = auditModel.getAuditRecordOptions(mi);
+ AuditMode auditMode = AuditMode.UNSET;
+ try
+ {
+ auditMode = onApplicationAudit(auditMode, auditInfo, source, description, key, args);
+ if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.SUCCESS))
+ {
+ auditDAO.audit(auditInfo);
+ }
+ }
+ catch (Throwable t)
+ {
+ auditMode = onError(auditMode, auditInfo, t, source, description, key, args);
+ if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL))
+ {
+ try
+ {
+ auditFailedDAO.audit(auditInfo);
+ }
+ catch (Throwable tt)
+ {
+ throw new AuditException("Failed to audit exception", new Object[] { tt }, t);
+ }
+ }
+ throw new AuditException("Application audit failed", t);
+ }
+ }
+
+ private AuditMode onApplicationAudit(AuditMode auditMode, AuditInfo auditInfo, String source, String description,
+ NodeRef key, Object... args)
+ {
+ AuditMode effectiveAuditMode = auditModel.beforeExecution(auditMode, source, description, key, args);
+
+ if (auditMode != AuditMode.NONE)
+ {
+ if(source.equals(SYSTEM_APPLICATION))
+ {
+ throw new AuditException("Application audit can not use the reserved identifier "+SYSTEM_APPLICATION);
+ }
+
+ auditInfo.setAuditApplication(source);
+ auditInfo.setAuditConfiguration(auditConfiguration);
+ auditInfo.setAuditMethod(null);
+ auditInfo.setAuditService(null);
+ auditInfo.setClientAddress(null);
+ auditInfo.setDate(new Date());
+ auditInfo.setFail(false);
+ auditInfo.setFiltered(false);
+ auditInfo.setHostAddress(auditHost);
+ auditInfo.setKeyGUID(null);
+ auditInfo.setKeyPropertiesAfter(null);
+ auditInfo.setKeyPropertiesBefore(null);
+ auditInfo.setKeyStore(null);
+ auditInfo.setMessage(description);
+ if (args != null)
+ {
+ Serializable[] serArgs = new Serializable[args.length];
+ for (int i = 0; i < args.length; i++)
+ {
+ if (args[i] == null)
+ {
+ serArgs[i] = null;
+ }
+ else if (args[i] instanceof Serializable)
+ {
+ serArgs[i] = (Serializable) args[i];
+ }
+ else
+ {
+ serArgs[i] = args[i].toString();
+ }
+ }
+ auditInfo.setMethodArguments(serArgs);
+ }
+ auditInfo.setPath(null);
+ auditInfo.setReturnObject(null);
+ auditInfo.setSessionId(null);
+ auditInfo.setThrowable(null);
+ auditInfo.setTxId(AlfrescoTransactionSupport.getTransactionId());
+ auditInfo.setUserIdentifier(AuthenticationUtil.getCurrentUserName());
+ }
+
+ return effectiveAuditMode;
+ }
+
+ private AuditMode onError(AuditMode auditMode, AuditInfo auditInfo, Throwable t, String source, String description,
+ NodeRef key, Object... args)
+ {
+ if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL))
+ {
+ auditInfo.setFail(true);
+ auditInfo.setThrowable(t);
+ }
+
+ return auditMode;
+
+ }
+}
diff --git a/source/java/org/alfresco/repo/audit/AuditConfiguration.java b/source/java/org/alfresco/repo/audit/AuditConfiguration.java
new file mode 100644
index 0000000000..66aba55205
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditConfiguration.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import java.io.InputStream;
+
+/**
+ * An audit configuration is xml content from an input stream.
+ *
+ * @author Andy Hind
+ */
+public interface AuditConfiguration
+{
+
+ /**
+ * Get the XML content for the configuration as a stream.
+ *
+ * @return
+ */
+ public abstract InputStream getInputStream();
+
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/audit/AuditConfigurationImpl.java b/source/java/org/alfresco/repo/audit/AuditConfigurationImpl.java
new file mode 100644
index 0000000000..1b41dfd699
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditConfigurationImpl.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import java.io.InputStream;
+
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * A class to read the audit configuration from the class path
+ *
+ * @author Andy Hind
+ */
+public class AuditConfigurationImpl implements InitializingBean, AuditConfiguration
+{
+
+ private String config;
+
+ public AuditConfigurationImpl()
+ {
+ super();
+ }
+
+ public void setConfig(String config)
+ {
+ this.config = config;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.getInputStream#getInputStream()
+ */
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.AuditConfiguration#getInputStream()
+ */
+ public InputStream getInputStream()
+ {
+ InputStream is = this.getClass().getClassLoader().getResourceAsStream(config);
+ return is;
+ }
+
+ public void afterPropertiesSet() throws Exception
+ {
+ // Read and set up the audit configuration
+
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/AuditDAO.java b/source/java/org/alfresco/repo/audit/AuditDAO.java
new file mode 100644
index 0000000000..ef307c5f93
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditDAO.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+/**
+ * The interface to persist audit information.
+ *
+ * @author Andy Hind
+ */
+public interface AuditDAO
+{
+ /**
+ * Create an audit entry.
+ *
+ * @param auditInfo
+ */
+ public void audit(AuditInfo auditInfo);
+}
diff --git a/source/java/org/alfresco/repo/audit/AuditException.java b/source/java/org/alfresco/repo/audit/AuditException.java
new file mode 100644
index 0000000000..c76cac4e55
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+
+/**
+ * Audit related exceptions.
+ *
+ * @author Andy Hind
+ */
+public class AuditException extends AlfrescoRuntimeException
+{
+
+ /**
+ * Comment for serialVersionUID
+ */
+ private static final long serialVersionUID = -7947190775692164588L;
+
+ public AuditException(String msgId)
+ {
+ super(msgId);
+ }
+
+ public AuditException(String msgId, Object[] msgParams)
+ {
+ super(msgId, msgParams);
+ }
+
+ public AuditException(String msgId, Throwable cause)
+ {
+ super(msgId, cause);
+ }
+
+ public AuditException(String msgId, Object[] msgParams, Throwable cause)
+ {
+ super(msgId, msgParams, cause);
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/AuditInfo.java b/source/java/org/alfresco/repo/audit/AuditInfo.java
new file mode 100644
index 0000000000..f62e57a92c
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditInfo.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Date;
+import java.util.Map;
+
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.namespace.QName;
+import org.apache.log4j.Logger;
+
+/**
+ * A class to encapsulate audit information supplied to the DAO layer.
+ *
+ * Null entries should be stored.
+ *
+ * @author Andy Hind
+ */
+public class AuditInfo
+{
+ private static Logger s_logger = Logger.getLogger(AuditInfo.class);
+
+ /**
+ * The user identifier for the person who caused this audit entry
+ */
+ private String userIdentifier;
+
+ /**
+ * The date for this audit entry
+ */
+ private Date date;
+
+ /**
+ * The transaction id in which this entry was made
+ */
+ private String txId;
+
+ /**
+ * The session is for this action
+ */
+ private String sessionId;
+
+ /**
+ * The store in which the action occured.
+ */
+ private StoreRef keyStore;
+
+ /**
+ * For a node ref, the node for the action.
+ */
+ private String keyGUID;
+
+ /**
+ * The path of the key
+ */
+ private String keyPath;
+
+ /**
+ * The audit application
+ * Internal uses the "System" key and will only audit method information.
+ */
+ private String auditApplication;
+
+ /**
+ * The service holding the audited method.
+ */
+ private String auditService;
+
+ /**
+ * The name of the audited method.
+ */
+ private String auditMethod;
+
+ /**
+ * Did this entry passa filter?
+ * If false - all entries were being recorded.
+ */
+ private boolean filtered;
+
+ /**
+ * The audit configuration in use at the time.
+ */
+ private AuditConfiguration auditConfiguration;
+
+ /**
+ * The object returned by the audited method.
+ */
+ private Serializable returnObject;
+
+ /**
+ * The arguments to the audited method.
+ */
+ private Serializable[] methodArguments;
+
+ /**
+ * Any Exception thrown by the audited method.
+ */
+ private Throwable throwable;
+
+ /**
+ * Did the audited method throw an exception?
+ */
+ private boolean fail;
+
+ /**
+ * The host address for where the audit was generated.
+ */
+ private InetAddress hostAddress;
+
+ private static InetAddress s_hostAddress;
+
+ /**
+ * The client address causing the audit
+ */
+ private InetAddress clientAddress;
+
+ /**
+ * The properties of the key node before the method execution.
+ */
+ private Map keyPropertiesBefore;
+
+ /**
+ * The properties of the key node after the method execution.
+ */
+ private Map keyPropertiesAfter;
+
+ /**
+ * For general auditing - the audit message.
+ */
+ private String message;
+
+ static
+ {
+ try
+ {
+ s_hostAddress = InetAddress.getLocalHost();
+ }
+ catch (UnknownHostException e)
+ {
+ s_logger.error(e);
+ s_hostAddress = null;
+ }
+ }
+
+ public AuditInfo(AuditConfiguration auditConfiguration)
+ {
+ super();
+ // Add default information
+ userIdentifier = AuthenticationUtil.getCurrentUserName();
+ date = new Date();
+ txId = AlfrescoTransactionSupport.getTransactionId();
+ sessionId = "Unavailable";
+ hostAddress = s_hostAddress;
+ }
+
+ public String getAuditApplication()
+ {
+ return auditApplication;
+ }
+
+ public void setAuditApplication(String auditApplication)
+ {
+ this.auditApplication = auditApplication;
+ }
+
+ public AuditConfiguration getAuditConfiguration()
+ {
+ return auditConfiguration;
+ }
+
+ public void setAuditConfiguration(AuditConfiguration auditConfiguration)
+ {
+ this.auditConfiguration = auditConfiguration;
+ }
+
+ public String getAuditMethod()
+ {
+ return auditMethod;
+ }
+
+ public void setAuditMethod(String auditMethod)
+ {
+ this.auditMethod = auditMethod;
+ }
+
+ public String getAuditService()
+ {
+ return auditService;
+ }
+
+ public void setAuditService(String auditService)
+ {
+ this.auditService = auditService;
+ }
+
+ public InetAddress getClientAddress()
+ {
+ return clientAddress;
+ }
+
+ public void setClientAddress(InetAddress clientAddress)
+ {
+ this.clientAddress = clientAddress;
+ }
+
+ public Date getDate()
+ {
+ return date;
+ }
+
+ public void setDate(Date date)
+ {
+ this.date = date;
+ }
+
+ public boolean isFail()
+ {
+ return fail;
+ }
+
+ public void setFail(boolean fail)
+ {
+ this.fail = fail;
+ }
+
+ public boolean isFiltered()
+ {
+ return filtered;
+ }
+
+ public void setFiltered(boolean filtered)
+ {
+ this.filtered = filtered;
+ }
+
+ public InetAddress getHostAddress()
+ {
+ return hostAddress;
+ }
+
+ public void setHostAddress(InetAddress hostAddress)
+ {
+ this.hostAddress = hostAddress;
+ }
+
+ public String getKeyGUID()
+ {
+ return keyGUID;
+ }
+
+ public void setKeyGUID(String keyGUID)
+ {
+ this.keyGUID = keyGUID;
+ }
+
+ public Map getKeyPropertiesAfter()
+ {
+ return keyPropertiesAfter;
+ }
+
+ public void setKeyPropertiesAfter(Map keyPropertiesAfter)
+ {
+ this.keyPropertiesAfter = keyPropertiesAfter;
+ }
+
+ public Map getKeyPropertiesBefore()
+ {
+ return keyPropertiesBefore;
+ }
+
+ public void setKeyPropertiesBefore(Map keyPropertiesBefore)
+ {
+ this.keyPropertiesBefore = keyPropertiesBefore;
+ }
+
+ public StoreRef getKeyStore()
+ {
+ return keyStore;
+ }
+
+ public void setKeyStore(StoreRef keyStore)
+ {
+ this.keyStore = keyStore;
+ }
+
+ public String getMessage()
+ {
+ return message;
+ }
+
+ public void setMessage(String message)
+ {
+ this.message = message;
+ }
+
+ public Serializable[] getMethodArguments()
+ {
+ return methodArguments;
+ }
+
+ public void setMethodArguments(Serializable[] methodArguments)
+ {
+ this.methodArguments = methodArguments;
+ }
+
+ public String getPath()
+ {
+ return keyPath;
+ }
+
+ public void setPath(String keyPath)
+ {
+ this.keyPath = keyPath;
+ }
+
+ public Serializable getReturnObject()
+ {
+ return returnObject;
+ }
+
+ public void setReturnObject(Serializable returnObject)
+ {
+ this.returnObject = returnObject;
+ }
+
+ public String getSessionId()
+ {
+ return sessionId;
+ }
+
+ public void setSessionId(String sessionId)
+ {
+ this.sessionId = sessionId;
+ }
+
+ public Throwable getThrowable()
+ {
+ return throwable;
+ }
+
+ public void setThrowable(Throwable throwable)
+ {
+ this.throwable = throwable;
+ }
+
+ public String getTxId()
+ {
+ return txId;
+ }
+
+ public void setTxId(String txId)
+ {
+ this.txId = txId;
+ }
+
+ public String getUserIdentifier()
+ {
+ return userIdentifier;
+ }
+
+ public void setUserIdentifier(String userIdentifier)
+ {
+ this.userIdentifier = userIdentifier;
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java b/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java
new file mode 100644
index 0000000000..3ba28246c3
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+/**
+ * A method interceptor to wrap method invocations with auditing.
+ *
+ * A single instance is used to wrap all services. If the single instance is disabled
+ * no auditing will be carried out and there will be minimal overhead.
+ *
+ * @author Andy Hind
+ */
+public class AuditMethodInterceptor implements MethodInterceptor
+{
+ //private static Log s_logger = LogFactory.getLog(AuditMethodInterceptor.class);
+
+ private AuditComponent auditComponent;
+
+ private boolean disabled = false;
+
+ public AuditMethodInterceptor()
+ {
+ super();
+ }
+
+ public void setDisabled(boolean disabled)
+ {
+ this.disabled = disabled;
+ }
+
+ public void setAuditComponent(AuditComponent auditComponent)
+ {
+ this.auditComponent = auditComponent;
+ }
+
+ public Object invoke(MethodInvocation mi) throws Throwable
+ {
+ if(disabled)
+ {
+ return mi.proceed();
+ }
+ else
+ {
+ return auditComponent.audit(mi);
+ }
+
+ }
+}
diff --git a/source/java/org/alfresco/repo/audit/AuditMode.java b/source/java/org/alfresco/repo/audit/AuditMode.java
new file mode 100644
index 0000000000..eab99ed6f4
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditMode.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import org.alfresco.repo.audit.model.AuditModelException;
+
+/**
+ * An enum to specify the audit mode:
+ *
+ *
+ *
ALL - all calls are audited
+ *
SUCCESS - only successful calls are audited (audited in the same TX)
+ *
FAIL - only fail calls are audited (in a new transaction)
+ *
NONE - noting is audited
+ *
UNSET
+ *
+ *
+ * The mode is inherited from containers if nothing is specified
+ *
+ * @author Andy Hind
+ */
+public enum AuditMode
+{
+ ALL, SUCCESS, FAIL, NONE, UNSET;
+
+ public static AuditMode getAuditMode(String value)
+ {
+ if(value.equalsIgnoreCase("all"))
+ {
+ return AuditMode.ALL;
+ }
+ else if(value.equalsIgnoreCase("success"))
+ {
+ return AuditMode.SUCCESS;
+ }
+ else if(value.equalsIgnoreCase("fail"))
+ {
+ return AuditMode.FAIL;
+ }
+ else if(value.equalsIgnoreCase("none"))
+ {
+ return AuditMode.NONE;
+ }
+ else if(value.equalsIgnoreCase("unset"))
+ {
+ return AuditMode.UNSET;
+ }
+ else
+ {
+ throw new AuditModelException("Invalid audit mode: "+value);
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/audit/AuditModel.java b/source/java/org/alfresco/repo/audit/AuditModel.java
new file mode 100644
index 0000000000..451ae9e12f
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditModel.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+
+/**
+ * API for querying the audit model
+ *
+ * @author Andy Hind
+ */
+public interface AuditModel extends ApplicationAuditModel, MethodAuditModel
+{
+ /**
+ * Constants for reading the xml model definition.
+ */
+
+ /* package */final static String NAME_SPACE = "http://www.alfresco.org/model/audit/1.0";
+
+ /* package */final static String EL_AUDIT = "Audit";
+
+ /* package */final static String EL_RECORD_OPTIONS = "RecordOptions";
+
+ /* package */final static String EL_RECORD_PATH = "recordPath";
+
+ /* package */final static String EL_RECORD_FILTERS = "recordFilters";
+
+ /* package */final static String EL_RECORD_SER_RETURN_VAL = "recordSerializedReturnValue";
+
+ /* package */final static String EL_RECORD_SER_EX = "recordSerializedExceptions";
+
+ /* package */final static String EL_RECORD_SER_ARGS = "recordSerializedMethodArguments";
+
+ /* package */final static String EL_RECORD_SER_PROP_BEFORE = "recordSerializedKeyPropertiesBeforeInvocation";
+
+ /* package */final static String EL_RECORD_SER_PROP_AFTER = "recordSerializedKeyPropertiesAferInvocation";
+
+ /* package */final static String EL_FILTER = "Filter";
+
+ /* package */final static String EL_METHOD = "Method";
+
+ /* package */final static String EL_SERVICE = "Service";
+
+ /* package */final static String EL_APPLICATION = "Application";
+
+ /* package */final static String EL_EXPRESSION = "Expression";
+
+ /* package */final static String EL_PARAMETER_NAME = "ParameterName";
+
+ /* package */final static String AT_MODE = "mode";
+
+ /* package */final static String AT_ENABLED = "enabled";
+
+ /* package */final static String AT_AUDIT_INTERNAL = "auditInternal";
+
+ /* package */final static String AT_NAME = "name";
+
+ /* package */final static String AT_INVERT = "invert";
+
+ /* package */final static String AT_TYPE = "type";
+}
diff --git a/source/java/org/alfresco/repo/audit/AuditServiceImpl.java b/source/java/org/alfresco/repo/audit/AuditServiceImpl.java
new file mode 100644
index 0000000000..b89f855f3c
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/AuditServiceImpl.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import javax.transaction.UserTransaction;
+
+import org.alfresco.service.cmr.audit.AuditService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.ApplicationContextHelper;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * The implementation of the AuditService for application auditing.
+ *
+ * @author Andy Hind
+ */
+public class AuditServiceImpl implements AuditService
+{
+ private AuditComponent auditComponent;
+
+ public AuditServiceImpl()
+ {
+ super();
+ }
+
+ public void setAuditComponent(AuditComponent auditComponent)
+ {
+ this.auditComponent = auditComponent;
+ }
+
+ public void audit(String source, String description)
+ {
+ auditComponent.audit(source, description, null, (Object[]) null);
+ }
+
+ public void audit(String source, String description, NodeRef key)
+ {
+ auditComponent.audit(source, description, key, (Object[]) null);
+ }
+
+ public void audit(String source, String description, Object... args)
+ {
+ auditComponent.audit(source, description, null, args);
+ }
+
+ public void audit(String source, String description, NodeRef key, Object... args)
+ {
+ auditComponent.audit(source, description, key, args);
+ }
+
+ public static void main(String[] args) throws Exception
+ {
+ ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
+ AuditService as = (AuditService) ctx.getBean("AuditService");
+
+ TransactionService txs = (TransactionService) ctx.getBean("transactionComponent");
+ UserTransaction tx = txs.getUserTransaction();
+ tx.begin();
+ as.audit("AuditedApp", "First");
+ as.audit("AuditedApp", "Second", new NodeRef(new StoreRef("test", "audit"), "id"));
+ as.audit("AuditedApp", "Third", new Object[]{"one", "two", "three"});
+ as.audit("AuditedApp", "Fourth", new NodeRef(new StoreRef("test", "audit"), "id"), new Object[]{"one", "two", "three"});
+
+ as.audit("UnAuditedApp", "First");
+ as.audit("UnAuditedApp", "Second", new NodeRef(new StoreRef("test", "audit"), "id"));
+ as.audit("UnAuditedApp", "Third", new Object[]{"one", "two", "three"});
+ as.audit("UnAuditedApp", "Fourth", new NodeRef(new StoreRef("test", "audit"), "id"), new Object[]{"one", "two", "three"});
+
+ tx.commit();
+
+ }
+}
diff --git a/source/java/org/alfresco/repo/audit/MethodAuditModel.java b/source/java/org/alfresco/repo/audit/MethodAuditModel.java
new file mode 100644
index 0000000000..37b1200173
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/MethodAuditModel.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+public interface MethodAuditModel
+{
+ /**
+ * Report if audit behaviour can be determined before the method call
+ *
+ * @param auditState,
+ * @param mi
+ * @return
+ */
+ public AuditMode beforeExecution(AuditMode auditMode, MethodInvocation mi);
+
+ /**
+ * Report if audit behaviour can be determined after the method call
+ *
+ * @param auditState,
+ * @param mi
+ * @return
+ */
+ public AuditMode afterExecution(AuditMode auditMode, MethodInvocation mi);
+
+ /**
+ * Report if audit behaviour should be invoked on error. It could be we look at the error and filter - this is not supported at the moment.
+ *
+ * @param auditState,
+ * @param mi
+ * @return
+ */
+ public AuditMode onError(AuditMode auditMode, MethodInvocation mi);
+
+ /**
+ * Get the optional parameters that are to be recorded
+ *
+ * @param mi
+ * @return
+ */
+ public RecordOptions getAuditRecordOptions(MethodInvocation mi);
+}
diff --git a/source/java/org/alfresco/repo/audit/PublicServiceIdentifier.java b/source/java/org/alfresco/repo/audit/PublicServiceIdentifier.java
new file mode 100644
index 0000000000..072b3a2931
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/PublicServiceIdentifier.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+/**
+ * This defines the API to identify the public service upon which a method invocation has been made.
+ *
+ * @author Andy Hind
+ */
+public interface PublicServiceIdentifier
+{
+ /**
+ * Get the name of the public service for the method invocation.
+ *
+ * @param mi
+ * @return
+ */
+ public String getPublicServiceName(MethodInvocation mi);
+}
diff --git a/source/java/org/alfresco/repo/audit/PublicServiceIdentifierImpl.java b/source/java/org/alfresco/repo/audit/PublicServiceIdentifierImpl.java
new file mode 100644
index 0000000000..d39f3c4881
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/PublicServiceIdentifierImpl.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.alfresco.service.PublicService;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+
+/**
+ * Identify public services by method invocation. Look ups are cached on a thread local as they are quite expensive. All public service names end with "Service" and start with
+ * capital letter. This pattern is used to filter bean names. TODO: Look at pulling out all the mappings at start up.
+ *
+ * @author Andy Hind
+ */
+public class PublicServiceIdentifierImpl implements PublicServiceIdentifier, BeanFactoryPostProcessor
+{
+ private static Log s_logger = LogFactory.getLog(PublicServiceIdentifierImpl.class);
+
+ private static ThreadLocal> methodToServiceMap = new ThreadLocal>();
+
+ private ConfigurableListableBeanFactory beanFactory;
+
+ public PublicServiceIdentifierImpl()
+ {
+ super();
+ }
+
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
+ {
+ this.beanFactory = beanFactory;
+ }
+
+ public String getPublicServiceName(MethodInvocation mi)
+ {
+ return getServiceName(mi);
+ }
+
+ /**
+ * Cache service name look up.
+ *
+ * @param mi
+ * @return
+ * @throws BeansException
+ */
+ private String getServiceName(MethodInvocation mi) throws BeansException
+ {
+ if (methodToServiceMap.get() == null)
+ {
+ methodToServiceMap.set(new HashMap());
+ }
+ Method method = mi.getMethod();
+ String serviceName = methodToServiceMap.get().get(method);
+ if (serviceName == null)
+ {
+ serviceName = getServiceNameImpl(mi);
+ methodToServiceMap.get().put(method, serviceName);
+ }
+ else
+ {
+ if (s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Cached look up for " + serviceName + "." + method.getName());
+ }
+ }
+ return serviceName;
+ }
+
+ /**
+ * Do the look up by interface type.
+ *
+ * @param mi
+ * @return
+ * @throws BeansException
+ */
+
+ private String getServiceNameImpl(MethodInvocation mi) throws BeansException
+ {
+ Class clazz = mi.getThis().getClass();
+ while (clazz != null)
+ {
+ Class[] interfaces = clazz.getInterfaces();
+ for (Class iFace : interfaces)
+ {
+ Class publicServiceInterface = findPublicService(iFace);
+ if (publicServiceInterface != null)
+ {
+ Map beans = beanFactory.getBeansOfType(publicServiceInterface);
+ Iterator iter = beans.entrySet().iterator();
+ while (iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String serviceName = (String) entry.getKey();
+ if ((serviceName.endsWith("Service"))
+ && (Character.isUpperCase(serviceName.charAt(0)))
+ && !serviceName.equals("DescriptorService"))
+ {
+ return serviceName;
+ }
+ }
+ }
+
+ }
+ clazz = clazz.getSuperclass();
+ }
+ return null;
+ }
+
+ /**
+ * We use a marker annotation to identify public interfaces.
+ * The interfaces have to be walked to determine if a public interface is implemented.
+ *
+ * Only one public service interface is expected.
+ *
+ * @param clazz
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ private Class findPublicService(Class clazz)
+ {
+ if (!clazz.isInterface())
+ {
+ return null;
+ }
+
+ if (clazz.isAnnotationPresent(PublicService.class))
+ {
+ return clazz;
+ }
+
+ Class[] classes = clazz.getInterfaces();
+ for(Class implemented: classes)
+ {
+ Class answer = findPublicService(implemented);
+ if(answer != null)
+ {
+ return answer;
+ }
+ }
+ return null;
+
+ }
+}
diff --git a/source/java/org/alfresco/repo/audit/RecordOptions.java b/source/java/org/alfresco/repo/audit/RecordOptions.java
new file mode 100644
index 0000000000..f1194bef20
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/RecordOptions.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit;
+
+import org.alfresco.repo.audit.model.TrueFalseUnset;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.dom4j.Element;
+
+public interface RecordOptions
+{
+ public TrueFalseUnset getRecordFilters();
+
+ public TrueFalseUnset getRecordPath();
+
+ public TrueFalseUnset getRecordSerializedExceptions();
+
+ public TrueFalseUnset getRecordSerializedKeyPropertiesAfterEvaluation();
+
+ public TrueFalseUnset getRecordSerializedKeyPropertiesBeforeEvaluation();
+
+ public TrueFalseUnset getRecordSerializedMethodArguments();
+
+ public TrueFalseUnset getRecordSerializedReturnValue();
+}
diff --git a/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml b/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml
new file mode 100644
index 0000000000..17ff8452cd
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select
+ audit_date
+ from
+ org.alfresco.repo.audit.hibernate.AuditDateImpl as audit_date
+ where
+ audit_date.id = (select max(audit_date_2.id) from org.alfresco.repo.audit.hibernate.AuditDateImpl as audit_date_2)
+
+
+
+ select
+ audit_config
+ from
+ org.alfresco.repo.audit.hibernate.AuditConfigImpl as audit_config
+ where
+ audit_config.id = (select max(audit_config_2.id) from org.alfresco.repo.audit.hibernate.AuditConfigImpl as audit_config_2)
+
+
+
+ select
+ audit_store
+ from
+ org.alfresco.repo.audit.hibernate.AuditSourceImpl as audit_store
+ where
+ audit_store.application = :application and
+ audit_store.service is null and
+ audit_store.method is null
+
+
+
+ select
+ audit_store
+ from
+ org.alfresco.repo.audit.hibernate.AuditSourceImpl as audit_store
+ where
+ audit_store.application = :application and
+ audit_store.service = :service and
+ audit_store.method = :method
+
+
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditConfig.java b/source/java/org/alfresco/repo/audit/hibernate/AuditConfig.java
new file mode 100644
index 0000000000..62369dfd4b
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/hibernate/AuditConfig.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.hibernate;
+
+
+/**
+ * Hibernate interface for audit configuration persistence.
+ *
+ * @author Andy Hind
+ */
+public interface AuditConfig
+{
+ /**
+ * Get the content URL for a copy of the configuration file.
+ *
+ * @return
+ */
+ public abstract String getConfigURL();
+
+ /**
+ * Get the surrogate key for the configuration entry.
+ * The creation is managed by hibernate.
+ *
+ * @return
+ */
+ public abstract long getId();
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java
new file mode 100644
index 0000000000..6e86b0b8ee
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.hibernate;
+
+import org.alfresco.util.EqualsHelper;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.springframework.beans.factory.InitializingBean;
+
+public class AuditConfigImpl implements AuditConfig, InitializingBean
+{
+ /**
+ * The hibernate generated internal key.
+ */
+ private long id;
+
+ /**
+ * The URL to the content that contains the configuration file
+ */
+ private String configURL;
+
+ public AuditConfigImpl()
+ {
+ super();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditContig#getConfigURL()
+ */
+ public String getConfigURL()
+ {
+ return configURL;
+ }
+
+ public void setConfigURL(String configURL)
+ {
+ this.configURL = configURL;
+ }
+
+
+
+ public void afterPropertiesSet() throws Exception
+ {
+ // Read the audit configuration
+
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditContig#getId()
+ */
+ public long getId()
+ {
+ return id;
+ }
+
+ /**
+ * Internal setter for hibernate.
+ *
+ * @param id
+ */
+
+ @SuppressWarnings("unused")
+ private void setId(long id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Helper method to get the latest audit config
+ */
+ public static AuditConfigImpl getLatestConfig(Session session)
+ {
+ Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_LAST_AUDIT_CONFIG);
+ return (AuditConfigImpl) query.uniqueResult();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if(!(o instanceof AuditConfigImpl))
+ {
+ return false;
+ }
+ AuditConfigImpl other = (AuditConfigImpl)o;
+ return EqualsHelper.nullSafeEquals(this.configURL, other.configURL);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return configURL == null ? 0 : configURL.hashCode();
+ }
+
+
+}
diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditDate.java b/source/java/org/alfresco/repo/audit/hibernate/AuditDate.java
new file mode 100644
index 0000000000..74f0b36ee7
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/hibernate/AuditDate.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.hibernate;
+
+import java.util.Date;
+
+/**
+ * Hibernate date dimension for audit roll ups
+ *
+ * @author Andy Hind
+ */
+public interface AuditDate
+{
+
+ /**
+ * Get the date
+ *
+ * @return
+ */
+ public abstract Date getDate();
+
+ /**
+ * Get the day of the year.
+ *
+ * @return
+ */
+ public abstract int getDayOfYear();
+
+ /**
+ * Get the day of the month.
+ *
+ * @return
+ */
+ public abstract int getDayOfMonth();
+
+ /**
+ * Get the day of the week
+ *
+ * @return
+ */
+ public abstract int getDayOfWeek();
+
+ /**
+ * Get the half year;
+ *
+ * @return
+ */
+ public abstract int getHalfYear();
+
+
+ /**
+ * Get the surrogate key
+ *
+ * @return
+ */
+ public abstract long getId();
+
+ /**
+ * Get the month
+ *
+ * @return
+ */
+ public abstract int getMonth();
+
+ /**
+ * Get the quarter
+ *
+ * @return
+ */
+ public abstract int getQuarter();
+
+ /**
+ * Get the week of the month.
+ *
+ * @return
+ */
+ public abstract int getWeekOfMonth();
+
+ /**
+ * Get the week of the year.
+ *
+ * @return
+ */
+ public abstract int getWeekOfYear();
+
+ /**
+ * Get the year.
+ * @return
+ */
+ public abstract int getYear();
+
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java
new file mode 100644
index 0000000000..a15e9c85da
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.hibernate;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.alfresco.util.EqualsHelper;
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+/**
+ * Hibernate persistence for a date dimension.
+ *
+ * @author Andy Hind
+ */
+public class AuditDateImpl implements AuditDate
+{
+ /**
+ * Surrogate key
+ */
+ private long id;
+
+ /**
+ * The date
+ */
+ private Date date;
+
+ /**
+ * The day
+ */
+ private int dayOfYear;
+
+ /**
+ * Day of month.
+ */
+ private int dayOfMonth;
+
+ /**
+ * The day of the week
+ */
+ private int dayOfWeek;
+
+ /**
+ * The week in the year
+ */
+ private int weekOfYear;
+
+ /**
+ * The week of the month
+ */
+ private int weekOfMonth;
+
+ /**
+ * The month in the year
+ */
+ private int month;
+
+ /**
+ * The quarter in the year
+ */
+ private int quarter;
+
+ /**
+ * The half year in the year
+ */
+ private int halfYear;
+
+ /**
+ * The year
+ */
+ private int year;
+
+ protected AuditDateImpl()
+ {
+ super();
+ }
+
+ public AuditDateImpl(Date date)
+ {
+ super();
+ setDate(date);
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getDate()
+ */
+ public Date getDate()
+ {
+ return date;
+ }
+
+ public void setDate(Date date)
+ {
+ Calendar cal = GregorianCalendar.getInstance();
+ cal.setTime(date);
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ this.date = cal.getTime();
+
+ this.setDayOfYear(cal.get(Calendar.DAY_OF_YEAR));
+ this.setDayOfMonth(cal.get(Calendar.DAY_OF_MONTH));
+ this.setDayOfWeek(cal.get(Calendar.DAY_OF_WEEK));
+ this.setMonth(cal.get(Calendar.MONTH));
+ this.setHalfYear(getMonth() <= Calendar.JUNE ? 0 : 1);
+ this.setQuarter((getMonth()/3));
+ this.setWeekOfMonth(cal.get(Calendar.WEEK_OF_MONTH));
+ this.setWeekOfYear(cal.get(Calendar.WEEK_OF_YEAR));
+ this.setYear(cal.get(Calendar.YEAR));
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getDayOfYear()
+ */
+ public int getDayOfYear()
+ {
+ return dayOfYear;
+ }
+
+ protected void setDayOfYear(int dayOfYear)
+ {
+ this.dayOfYear = dayOfYear;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getDayOfMonth()
+ */
+ public int getDayOfMonth()
+ {
+ return dayOfMonth;
+ }
+
+ protected void setDayOfMonth(int dayOfMonth)
+ {
+ this.dayOfMonth = dayOfMonth;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getDayOfWeek()
+ */
+ public int getDayOfWeek()
+ {
+ return dayOfWeek;
+ }
+
+ protected void setDayOfWeek(int dayOfWeek)
+ {
+ this.dayOfWeek = dayOfWeek;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getHalfYear()
+ */
+ public int getHalfYear()
+ {
+ return halfYear;
+ }
+
+ protected void setHalfYear(int halfYear)
+ {
+ this.halfYear = halfYear;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getId()
+ */
+ public long getId()
+ {
+ return id;
+ }
+
+ protected void setId(long id)
+ {
+ this.id = id;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getMonth()
+ */
+ public int getMonth()
+ {
+ return month;
+ }
+
+ protected void setMonth(int month)
+ {
+ this.month = month;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getQuarter()
+ */
+ public int getQuarter()
+ {
+ return quarter;
+ }
+
+ protected void setQuarter(int quarter)
+ {
+ this.quarter = quarter;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getWeekOfMonth()
+ */
+ public int getWeekOfMonth()
+ {
+ return weekOfMonth;
+ }
+
+ protected void setWeekOfMonth(int weekOfMonth)
+ {
+ this.weekOfMonth = weekOfMonth;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getWeekOfYear()
+ */
+ public int getWeekOfYear()
+ {
+ return weekOfYear;
+ }
+
+ protected void setWeekOfYear(int weekOfYear)
+ {
+ this.weekOfYear = weekOfYear;
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.audit.hibernate.AuditDate#getYear()
+ */
+ public int getYear()
+ {
+ return year;
+ }
+
+ protected void setYear(int year)
+ {
+ this.year = year;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if(this == o)
+ {
+ return true;
+ }
+ if(!(o instanceof AuditDateImpl))
+ {
+ return false;
+ }
+ AuditDateImpl that = (AuditDateImpl)o;
+ return EqualsHelper.nullSafeEquals(this.date, that.date);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return this.date.hashCode();
+ }
+
+ /**
+ * Helper method to get the latest audit date
+ */
+ public static AuditDateImpl getLatestDate(Session session)
+ {
+ Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_LAST_AUDIT_DATE);
+ return (AuditDateImpl) query.uniqueResult();
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditFact.java b/source/java/org/alfresco/repo/audit/hibernate/AuditFact.java
new file mode 100644
index 0000000000..17d08204d8
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/hibernate/AuditFact.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.hibernate;
+
+import java.util.Date;
+
+public interface AuditFact
+{
+
+ public abstract String getArg1();
+
+ public abstract String getArg2();
+
+ public abstract String getArg3();
+
+ public abstract String getArg4();
+
+ public abstract String getArg5();
+
+ public abstract AuditConfig getAuditConfig();
+
+ public abstract AuditDate getAuditDate();
+
+ public abstract AuditSource getAuditSource();
+
+ public abstract String getClientInetAddress();
+
+ public abstract Date getDate();
+
+ public abstract String getException();
+
+ public abstract boolean isFail();
+
+ public abstract boolean isFiltered();
+
+ public abstract String getHostInetAddress();
+
+ public abstract long getId();
+
+ public abstract String getMessage();
+
+ public abstract String getNodeUUID();
+
+ public abstract String getPath();
+
+ public abstract String getReturnValue();
+
+ public abstract String getSerialisedURL();
+
+ public abstract String getSessionId();
+
+ public abstract String getStoreId();
+
+ public abstract String getStoreProtocol();
+
+ public abstract String getTransactionId();
+
+ public abstract String getUserId();
+
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java
new file mode 100644
index 0000000000..3ec7f4a4ad
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.hibernate;
+
+import java.util.Date;
+
+/**
+ * An Audit fact Rely on standard equals and hash code as they should all be unique.
+ *
+ * @author Andy Hind
+ */
+public class AuditFactImpl implements AuditFact
+{
+ private long id;
+
+ private AuditDate auditDate;
+
+ private AuditConfig auditConfig;
+
+ private AuditSource auditSource;
+
+ private String userId;
+
+ private Date date;
+
+ private String transactionId;
+
+ private String sessionId;
+
+ private String storeProtocol;
+
+ private String storeId;
+
+ private String nodeUUID;
+
+ private String path;
+
+ private boolean filtered;
+
+ private String returnValue;
+
+ private String arg1;
+
+ private String arg2;
+
+ private String arg3;
+
+ private String arg4;
+
+ private String arg5;
+
+ private boolean fail;
+
+ private String serialisedURL;
+
+ private String exception;
+
+ private String hostInetAddress;
+
+ private String clientInetAddress;
+
+ private String message;
+
+ public AuditFactImpl()
+ {
+ super();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getArg1()
+ */
+ public String getArg1()
+ {
+ return arg1;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setArg1(java.lang.String)
+ */
+ public void setArg1(String arg1)
+ {
+ this.arg1 = arg1;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getArg2()
+ */
+ public String getArg2()
+ {
+ return arg2;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setArg2(java.lang.String)
+ */
+ public void setArg2(String arg2)
+ {
+ this.arg2 = arg2;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getArg3()
+ */
+ public String getArg3()
+ {
+ return arg3;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setArg3(java.lang.String)
+ */
+ public void setArg3(String arg3)
+ {
+ this.arg3 = arg3;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getArg4()
+ */
+ public String getArg4()
+ {
+ return arg4;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setArg4(java.lang.String)
+ */
+ public void setArg4(String arg4)
+ {
+ this.arg4 = arg4;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getArg5()
+ */
+ public String getArg5()
+ {
+ return arg5;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setArg5(java.lang.String)
+ */
+ public void setArg5(String arg5)
+ {
+ this.arg5 = arg5;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getAuditConfig()
+ */
+ public AuditConfig getAuditConfig()
+ {
+ return auditConfig;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setAuditConfig(org.alfresco.repo.audit.hibernate.AuditConfig)
+ */
+ public void setAuditConfig(AuditConfig auditConfig)
+ {
+ this.auditConfig = auditConfig;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getAuditDate()
+ */
+ public AuditDate getAuditDate()
+ {
+ return auditDate;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setAuditDate(org.alfresco.repo.audit.hibernate.AuditDate)
+ */
+ public void setAuditDate(AuditDate auditDate)
+ {
+ this.auditDate = auditDate;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getAuditSource()
+ */
+ public AuditSource getAuditSource()
+ {
+ return auditSource;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setAuditSource(org.alfresco.repo.audit.hibernate.AuditSource)
+ */
+ public void setAuditSource(AuditSource auditSource)
+ {
+ this.auditSource = auditSource;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getClientInetAddress()
+ */
+ public String getClientInetAddress()
+ {
+ return clientInetAddress;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setClientInetAddress(java.net.InetAddress)
+ */
+ public void setClientInetAddress(String clientInetAddress)
+ {
+ this.clientInetAddress = clientInetAddress;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getDate()
+ */
+ public Date getDate()
+ {
+ return date;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setDate(java.util.Date)
+ */
+ public void setDate(Date date)
+ {
+ this.date = date;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getException()
+ */
+ public String getException()
+ {
+ return exception;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setException(java.lang.String)
+ */
+ public void setException(String exception)
+ {
+ this.exception = exception;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#isFail()
+ */
+ public boolean isFail()
+ {
+ return fail;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setFail(boolean)
+ */
+ public void setFail(boolean fail)
+ {
+ this.fail = fail;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#isFiltered()
+ */
+ public boolean isFiltered()
+ {
+ return filtered;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setFiltered(boolean)
+ */
+ public void setFiltered(boolean filtered)
+ {
+ this.filtered = filtered;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getHostInetAddress()
+ */
+ public String getHostInetAddress()
+ {
+ return hostInetAddress;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setHostInetAddress(java.net.InetAddress)
+ */
+ public void setHostInetAddress(String hostInetAddress)
+ {
+ this.hostInetAddress = hostInetAddress;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getId()
+ */
+ public long getId()
+ {
+ return id;
+ }
+
+ protected void setId(long id)
+ {
+ this.id = id;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getMessage()
+ */
+ public String getMessage()
+ {
+ return message;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setMessage(java.lang.String)
+ */
+ public void setMessage(String message)
+ {
+ this.message = message;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getNodeGUID()
+ */
+ public String getNodeUUID()
+ {
+ return nodeUUID;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setNodeGUID(java.lang.String)
+ */
+ public void setNodeUUID(String nodeUUID)
+ {
+ this.nodeUUID = nodeUUID;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getPath()
+ */
+ public String getPath()
+ {
+ return path;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setPath(java.lang.String)
+ */
+ public void setPath(String path)
+ {
+ this.path = path;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getReturnValue()
+ */
+ public String getReturnValue()
+ {
+ return returnValue;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setReturnValue(java.lang.String)
+ */
+ public void setReturnValue(String returnValue)
+ {
+ this.returnValue = returnValue;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getSerialisedURL()
+ */
+ public String getSerialisedURL()
+ {
+ return serialisedURL;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setSerialisedURL(java.lang.String)
+ */
+ public void setSerialisedURL(String serialisedURL)
+ {
+ this.serialisedURL = serialisedURL;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getSessionId()
+ */
+ public String getSessionId()
+ {
+ return sessionId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setSessionId(java.lang.String)
+ */
+ public void setSessionId(String sessionId)
+ {
+ this.sessionId = sessionId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getStoreId()
+ */
+ public String getStoreId()
+ {
+ return storeId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setStoreId(java.lang.String)
+ */
+ public void setStoreId(String storeId)
+ {
+ this.storeId = storeId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getStoreProtocol()
+ */
+ public String getStoreProtocol()
+ {
+ return storeProtocol;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setStoreProtocol(java.lang.String)
+ */
+ public void setStoreProtocol(String storeProtocol)
+ {
+ this.storeProtocol = storeProtocol;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getTransactionId()
+ */
+ public String getTransactionId()
+ {
+ return transactionId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setTransactionId(java.lang.String)
+ */
+ public void setTransactionId(String transactionId)
+ {
+ this.transactionId = transactionId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#getUserId()
+ */
+ public String getUserId()
+ {
+ return userId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.alfresco.repo.audit.hibernate.AuditFact#setUserId(java.lang.String)
+ */
+ public void setUserId(String userId)
+ {
+ this.userId = userId;
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditSource.java b/source/java/org/alfresco/repo/audit/hibernate/AuditSource.java
new file mode 100644
index 0000000000..17ac1c2ef0
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/hibernate/AuditSource.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.hibernate;
+
+public interface AuditSource
+{
+ public String getApplication();
+
+ public long getId();
+
+ public String getMethod();
+
+ public String getService();
+
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java
new file mode 100644
index 0000000000..927d042317
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.hibernate;
+
+import org.alfresco.util.EqualsHelper;
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+public class AuditSourceImpl implements AuditSource
+{
+ /**
+ * The surrogate key
+ */
+ private long id;
+
+ /**
+ * The auditing application (System for method audits)
+ */
+ private String application;
+
+ /**
+ * The audited service
+ */
+ private String service;
+
+ /**
+ * The audited method
+ */
+ private String method;
+
+ public AuditSourceImpl()
+ {
+ super();
+ }
+
+ public String getApplication()
+ {
+ return application;
+ }
+
+ public void setApplication(String application)
+ {
+ this.application = application;
+ }
+
+ public long getId()
+ {
+ return id;
+ }
+
+ protected void setId(long id)
+ {
+ this.id = id;
+ }
+
+ public String getMethod()
+ {
+ return method;
+ }
+
+ public void setMethod(String method)
+ {
+ this.method = method;
+ }
+
+ public String getService()
+ {
+ return service;
+ }
+
+ public void setService(String service)
+ {
+ this.service = service;
+ }
+
+ public static AuditSourceImpl getApplicationSource(Session session, String application)
+ {
+ Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE);
+ query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_APP, application);
+ return (AuditSourceImpl) query.uniqueResult();
+ }
+
+ public static AuditSourceImpl getApplicationSource(Session session, String application, String service,
+ String method)
+ {
+ Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_AUDIT_METHOD_SOURCE);
+ query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_APP, application);
+ query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_SER, service);
+ query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_MET, method);
+ return (AuditSourceImpl) query.uniqueResult();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if (!(o instanceof AuditSourceImpl))
+ {
+ return false;
+ }
+ AuditSourceImpl other = (AuditSourceImpl) o;
+ return EqualsHelper.nullSafeEquals(this.application, other.application)
+ && EqualsHelper.nullSafeEquals(this.service, other.service)
+ && EqualsHelper.nullSafeEquals(this.method, other.method);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = application.hashCode();
+ if(service != null)
+ {
+ hash = (hash * 37) + service.hashCode();
+ }
+ if(method != null)
+ {
+ hash = (hash * 37) + method.hashCode();
+ }
+ return hash;
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java b/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java
new file mode 100644
index 0000000000..b4bf563e18
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.hibernate;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.audit.AuditConfiguration;
+import org.alfresco.repo.audit.AuditDAO;
+import org.alfresco.repo.audit.AuditInfo;
+import org.alfresco.repo.content.AbstractContentStore;
+import org.alfresco.repo.content.ContentStore;
+import org.alfresco.repo.content.MimetypeMap;
+import org.alfresco.repo.transaction.TransactionalDao;
+import org.alfresco.service.cmr.repository.ContentReader;
+import org.alfresco.service.cmr.repository.ContentWriter;
+import org.alfresco.service.cmr.repository.datatype.Duration;
+import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.GUID;
+import org.hibernate.Session;
+import org.springframework.orm.hibernate3.HibernateCallback;
+import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
+
+/**
+ * Assumes mimetype and encoding sent to the content store (we are not saving this anywhere)
+ *
+ * @author Andy Hind
+ */
+public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, TransactionalDao
+{
+ public static final String QUERY_LAST_AUDIT_DATE = "audit.GetLatestAuditDate";
+
+ public static final String QUERY_LAST_AUDIT_CONFIG = "audit.GetLatestAuditConfig";
+
+ public static final String QUERY_AUDIT_APP_SOURCE = "audit.GetAuditSourceByApplication";
+
+ public static final String QUERY_AUDIT_METHOD_SOURCE = "audit.GetAuditSourceByApplicationServiceMethod";
+
+ public static final String QUERY_AUDIT_APP_SOURCE_APP = "application";
+
+ public static final String QUERY_AUDIT_APP_SOURCE_SER = "service";
+
+ public static final String QUERY_AUDIT_APP_SOURCE_MET = "method";
+
+ /** a uuid identifying this unique instance */
+ private String uuid;
+
+ private ContentStore contentStore;
+
+ private ThreadLocal auditConfiguration = new ThreadLocal();
+
+ private ThreadLocal auditConfigImplId = new ThreadLocal();
+
+ private ThreadLocal auditDateImplId = new ThreadLocal();
+
+ private ThreadLocal> sourceIds = new ThreadLocal>();
+
+ public HibernateAuditDAO()
+ {
+ super();
+ this.uuid = GUID.generate();
+ }
+
+ public ContentStore getContentStore()
+ {
+ return contentStore;
+ }
+
+ public void setContentStore(ContentStore contentStore)
+ {
+ this.contentStore = contentStore;
+ }
+
+ public void audit(AuditInfo auditInfo)
+ {
+ // Find/Build the configuraton entry
+ AuditConfigImpl auditConfig = getAuditConfig(auditInfo);
+
+ // Find/Build any dates
+ AuditDateImpl auditDate = getAuditDate(auditInfo);
+
+ // Find/Build the source
+ AuditSourceImpl auditSource = getAuditSource(auditInfo);
+
+ // Build the new audit fact information
+ AuditFactImpl auditFact = new AuditFactImpl();
+ auditFact.setAuditConfig(auditConfig);
+ auditFact.setAuditDate(auditDate);
+ auditFact.setAuditSource(auditSource);
+
+ // Properties
+
+ Serializable[] args = auditInfo.getMethodArguments();
+
+ if (args != null)
+ {
+ switch (args.length)
+ {
+ default:
+ case 5:
+ auditFact.setArg5(getStringOrNull(args[4]));
+ case 4:
+ auditFact.setArg4(getStringOrNull(args[3]));
+ case 3:
+ auditFact.setArg3(getStringOrNull(args[2]));
+ case 2:
+ auditFact.setArg2(getStringOrNull(args[1]));
+ case 1:
+ auditFact.setArg1(getStringOrNull(args[0]));
+ case 0:
+ }
+ }
+
+ auditFact.setClientInetAddress(auditInfo.getClientAddress() == null ? null : auditInfo.getClientAddress()
+ .toString());
+ auditFact.setDate(auditInfo.getDate());
+ auditFact.setException(auditInfo.getThrowable() == null ? null : auditInfo.getThrowable().getMessage());
+ auditFact.setFail(auditInfo.isFail());
+ auditFact.setFiltered(auditInfo.isFiltered());
+ auditFact.setHostInetAddress(auditInfo.getHostAddress() == null ? null : auditInfo.getHostAddress().toString());
+ auditFact.setMessage(auditInfo.getMessage());
+ auditFact.setNodeUUID(auditInfo.getKeyGUID());
+ auditFact.setPath(auditInfo.getPath());
+ auditFact.setReturnValue(auditInfo.getReturnObject() == null ? null : auditInfo.getReturnObject().toString());
+ // auditFact.setSerialisedURL()
+ auditFact.setSessionId(auditInfo.getSessionId());
+ if (auditInfo.getKeyStore() != null)
+ {
+ auditFact.setStoreId(auditInfo.getKeyStore().getIdentifier());
+ auditFact.setStoreProtocol(auditInfo.getKeyStore().getProtocol());
+ }
+ auditFact.setTransactionId(auditInfo.getTxId());
+ auditFact.setUserId(auditInfo.getUserIdentifier());
+
+ // Save
+ getSession().save(auditFact);
+
+ }
+
+ private String getStringOrNull(Object o)
+ {
+ if (o == null)
+ {
+ return null;
+ }
+ else
+ {
+ return o.toString();
+ }
+ }
+
+ private AuditSourceImpl getAuditSource(AuditInfo auditInfo)
+ {
+ AuditSourceImpl auditSourceImpl;
+
+ SourceKey sourceKey = new SourceKey(auditInfo.getAuditApplication(), auditInfo.getAuditService(), auditInfo.getAuditMethod());
+ if(sourceIds.get() == null)
+ {
+ sourceIds.set(new HashMap());
+ }
+ Long id = sourceIds.get().get(sourceKey);
+ if(id != null)
+ {
+ auditSourceImpl = (AuditSourceImpl) getSession().get(AuditSourceImpl.class, id.longValue());
+ if(auditSourceImpl != null)
+ {
+ return auditSourceImpl;
+ }
+ }
+
+ if ((auditInfo.getAuditService() != null)
+ && (auditInfo.getAuditService().length() > 0) && (auditInfo.getAuditMethod() != null)
+ && (auditInfo.getAuditMethod().length() > 0))
+ {
+ auditSourceImpl = AuditSourceImpl.getApplicationSource(getSession(), auditInfo.getAuditApplication(),
+ auditInfo.getAuditService(), auditInfo.getAuditMethod());
+ if (auditSourceImpl == null)
+ {
+ auditSourceImpl = new AuditSourceImpl();
+ auditSourceImpl.setApplication(auditInfo.getAuditApplication());
+ auditSourceImpl.setService(auditInfo.getAuditService());
+ auditSourceImpl.setMethod(auditInfo.getAuditMethod());
+ getSession().save(auditSourceImpl);
+ }
+ }
+ else
+ {
+ auditSourceImpl = AuditSourceImpl.getApplicationSource(getSession(), auditInfo.getAuditApplication());
+ if (auditSourceImpl == null)
+ {
+ auditSourceImpl = new AuditSourceImpl();
+ auditSourceImpl.setApplication(auditInfo.getAuditApplication());
+ getSession().save(auditSourceImpl);
+ }
+ }
+ sourceIds.get().put(sourceKey, Long.valueOf(auditSourceImpl.getId()));
+ return auditSourceImpl;
+ }
+
+ private AuditDateImpl getAuditDate(AuditInfo auditInfo)
+ {
+ Calendar cal = GregorianCalendar.getInstance();
+ cal.setTime(auditInfo.getDate());
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ Date required = cal.getTime();
+
+ AuditDateImpl auditDate;
+ if (auditDateImplId.get() == null)
+ {
+ auditDate = AuditDateImpl.getLatestDate(getSession());
+ if (auditDate == null)
+ {
+ // The first entry ever so we just make it
+ auditDate = new AuditDateImpl(auditInfo.getDate());
+ getSession().save(auditDate);
+ }
+ auditDateImplId.set(Long.valueOf(auditDate.getId()));
+ }
+ else
+ {
+ auditDate = (AuditDateImpl) getSession().get(AuditDateImpl.class, auditDateImplId.get().longValue());
+ if ((auditDate == null) || (!required.equals(auditDate.getDate())))
+ {
+ auditDate = AuditDateImpl.getLatestDate(getSession());
+ if (auditDate == null)
+ {
+ // The first entry ever so we just make it
+ auditDate = new AuditDateImpl(auditInfo.getDate());
+ getSession().save(auditDate);
+ }
+ auditDateImplId.set(Long.valueOf(auditDate.getId()));
+ }
+ }
+ while (!required.equals(auditDate.getDate()))
+ {
+ Date nextDate = Duration.add(auditDate.getDate(), new Duration("P1D"));
+ auditDate = new AuditDateImpl(nextDate);
+ getSession().save(auditDate);
+ auditDateImplId.set(Long.valueOf(auditDate.getId()));
+ }
+ return auditDate;
+ }
+
+ private AuditConfigImpl getAuditConfig(AuditInfo auditInfo)
+ {
+ AuditConfigImpl auditConfig;
+ if ((auditConfiguration.get() == null) || (auditConfiguration.get() != auditInfo.getAuditConfiguration()))
+ {
+ auditConfig = AuditConfigImpl.getLatestConfig(getSession());
+ if (auditConfig == null)
+ {
+ auditConfig = createNewAuditConfigImpl(auditInfo);
+ }
+ else
+ {
+ InputStream current = new BufferedInputStream(auditInfo.getAuditConfiguration().getInputStream());
+ ContentReader reader = contentStore.getReader(auditConfig.getConfigURL());
+ reader.setMimetype(MimetypeMap.MIMETYPE_XML);
+ reader.setEncoding("UTF-8");
+ InputStream last = new BufferedInputStream(reader.getContentInputStream());
+ int currentValue = -2;
+ int lastValue = -2;
+ try
+ {
+ while ((currentValue != -1) && (lastValue != -1) && (currentValue == lastValue))
+ {
+ currentValue = current.read();
+ lastValue = last.read();
+
+ }
+ }
+ catch (IOException e)
+ {
+ throw new AlfrescoRuntimeException(
+ "Failed to read and validate current audit configuration against the last", e);
+ }
+ if (currentValue != lastValue)
+ {
+ // Files are different - require a new entry
+ auditConfig = createNewAuditConfigImpl(auditInfo);
+ }
+ else
+ {
+ // No change
+ }
+ }
+ auditConfigImplId.set(Long.valueOf(auditConfig.getId()));
+ auditConfiguration.set(auditInfo.getAuditConfiguration());
+ }
+ else
+ {
+ auditConfig = (AuditConfigImpl) getSession()
+ .get(AuditConfigImpl.class, auditConfigImplId.get().longValue());
+ if (auditConfig == null)
+ {
+ auditConfig = createNewAuditConfigImpl(auditInfo);
+ }
+ }
+ return auditConfig;
+ }
+
+ private AuditConfigImpl createNewAuditConfigImpl(AuditInfo auditInfo)
+ {
+ AuditConfigImpl auditConfig = new AuditConfigImpl();
+ InputStream is = new BufferedInputStream(auditInfo.getAuditConfiguration().getInputStream());
+ String url = AbstractContentStore.createNewUrl();
+ ContentWriter writer = contentStore.getWriter(null, url);
+ writer.setMimetype(MimetypeMap.MIMETYPE_XML);
+ writer.setEncoding("UTF-8");
+ writer.putContent(is);
+ auditConfig.setConfigURL(url);
+ getSession().save(auditConfig);
+ return auditConfig;
+ }
+
+ /**
+ * Checks equality by type and uuid
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ else if (!(obj instanceof HibernateAuditDAO))
+ {
+ return false;
+ }
+ HibernateAuditDAO that = (HibernateAuditDAO) obj;
+ return this.uuid.equals(that.uuid);
+ }
+
+ /**
+ * @see #uuid
+ */
+ public int hashCode()
+ {
+ return uuid.hashCode();
+ }
+
+ /**
+ * Does this Session contain any changes which must be synchronized with the store?
+ *
+ * @return true => changes are pending
+ */
+ public boolean isDirty()
+ {
+ // create a callback for the task
+ HibernateCallback callback = new HibernateCallback()
+ {
+ public Object doInHibernate(Session session)
+ {
+ return session.isDirty();
+ }
+ };
+ // execute the callback
+ return ((Boolean) getHibernateTemplate().execute(callback)).booleanValue();
+ }
+
+ /**
+ * Just flushes the session
+ */
+ public void flush()
+ {
+ getSession().flush();
+ }
+
+ static class SourceKey
+ {
+ String application;
+
+ String service;
+
+ String method;
+
+ SourceKey(String application, String service, String method)
+ {
+ this.application = application;
+ this.service = service;
+ this.method = method;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if (!(this instanceof SourceKey))
+ {
+ return false;
+ }
+ SourceKey other = (SourceKey) o;
+ return EqualsHelper.nullSafeEquals(this.application, other.application)
+ && EqualsHelper.nullSafeEquals(this.service, other.service)
+ && EqualsHelper.nullSafeEquals(this.method, other.method);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = application.hashCode();
+ if (service != null)
+ {
+ hash = (hash * 37) + service.hashCode();
+ }
+ if (method != null)
+ {
+ hash = (hash * 37) + method.hashCode();
+ }
+ return hash;
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java b/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java
new file mode 100644
index 0000000000..7349e44be0
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc. Licensed under the Mozilla Public License version 1.1 with a permitted attribution clause. You may obtain a copy of the License at
+ * http://www.alfresco.org/legal/license.txt Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+package org.alfresco.repo.audit.model;
+
+import org.alfresco.repo.audit.AuditMode;
+import org.alfresco.repo.audit.AuditModel;
+import org.alfresco.repo.audit.PublicServiceIdentifier;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Attribute;
+import org.dom4j.Element;
+
+public abstract class AbstractAuditEntry
+{
+ private static Log s_logger = LogFactory.getLog(AbstractAuditEntry.class);
+
+ private RecordOptionsImpl recordOptions = null;
+
+ private AbstractFilter filter = null;
+
+ private AuditMode auditMode = AuditMode.UNSET;
+
+ private TrueFalseUnset enabled = TrueFalseUnset.UNSET;
+
+ private TrueFalseUnset auditInternal = TrueFalseUnset.UNSET;
+
+ private AbstractAuditEntry parent;
+
+ private PublicServiceIdentifier publicServiceIdentifier;
+
+ public AbstractAuditEntry()
+ {
+ super();
+ }
+
+ PublicServiceIdentifier getPublicServiceIdentifier()
+ {
+ return publicServiceIdentifier;
+ }
+
+ public void setPublicServiceIdentifier(PublicServiceIdentifier publicServiceIdentifier)
+ {
+ this.publicServiceIdentifier = publicServiceIdentifier;
+ }
+
+ void configure(AbstractAuditEntry parent, Element element, NamespacePrefixResolver namespacePrefixResolver)
+ {
+ this.parent = parent;
+
+ Attribute auditModeAttribute = element.attribute(AuditModel.AT_MODE);
+ if (auditModeAttribute != null)
+ {
+ auditMode = AuditMode.getAuditMode(auditModeAttribute.getValue());
+ }
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Audit Mode = "+auditMode);
+ }
+
+
+ Attribute enabledAttribute = element.attribute(AuditModel.AT_ENABLED);
+ if (enabledAttribute != null)
+ {
+ enabled = TrueFalseUnset.getTrueFalseUnset(enabledAttribute.getValue());
+ }
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Enabled = "+enabled);
+ }
+
+ Attribute auditInternalAttribute = element.attribute(AuditModel.AT_AUDIT_INTERNAL);
+ if (auditInternalAttribute != null)
+ {
+ auditInternal = TrueFalseUnset.getTrueFalseUnset(auditInternalAttribute.getValue());
+ }
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Audit Internal = "+auditInternal);
+ }
+
+ // Make record options
+ Element recordOptionElement = element.element(AuditModel.EL_RECORD_OPTIONS);
+ if (recordOptionElement != null)
+ {
+ recordOptions = new RecordOptionsImpl();
+ recordOptions.configure(recordOptionElement, namespacePrefixResolver);
+ }
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Record Options = "+recordOptions);
+ }
+
+ // Make filters
+ Element filterElement = element.element(AuditModel.EL_FILTER);
+ if (filterElement != null)
+ {
+ filter = AbstractFilter.createFilter(filterElement, namespacePrefixResolver);
+ }
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Filter = "+filter);
+ }
+
+ }
+
+ /* package */TrueFalseUnset getAuditInternal()
+ {
+ return auditInternal;
+ }
+
+ /* package */AuditMode getAuditMode()
+ {
+ return auditMode;
+ }
+
+ /* package */TrueFalseUnset getEnabled()
+ {
+ return enabled;
+ }
+
+ /* package */AbstractFilter getFilter()
+ {
+ return filter;
+ }
+
+ /* package */AbstractAuditEntry getParent()
+ {
+ return parent;
+ }
+
+ /* package */RecordOptionsImpl getRecordOptions()
+ {
+ return recordOptions;
+ }
+
+ protected AuditMode getEffectiveAuditMode()
+ {
+ AuditMode auditMode;
+ if (checkEnabled() == TrueFalseUnset.TRUE)
+ {
+ auditMode = getAuditModeOrParentAuditMode();
+ }
+ else
+ {
+ auditMode = AuditMode.NONE;
+ }
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("... Effective audit mode is = "+auditMode);
+ }
+ return auditMode;
+ }
+
+ private AuditMode getAuditModeOrParentAuditMode()
+ {
+ AuditMode auditMode = getAuditMode();
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("... ... audit mode is = "+auditMode);
+ }
+ if (auditMode == AuditMode.UNSET)
+ {
+ if (getParent() == null)
+ {
+ return AuditMode.UNSET;
+ }
+ else
+ {
+ return getParent().getAuditModeOrParentAuditMode();
+ }
+ }
+ else
+ {
+ return auditMode;
+ }
+
+ }
+
+ private TrueFalseUnset checkEnabled()
+ {
+ TrueFalseUnset effective = getEnabled();
+ if (getParent() != null)
+ {
+ if ((getParent().checkEnabled() == TrueFalseUnset.TRUE) && (effective != TrueFalseUnset.FALSE))
+ {
+ return TrueFalseUnset.TRUE;
+ }
+ }
+ else
+ {
+ if (effective == TrueFalseUnset.TRUE)
+ {
+ return TrueFalseUnset.TRUE;
+ }
+ }
+ return TrueFalseUnset.FALSE;
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/AbstractFilter.java b/source/java/org/alfresco/repo/audit/model/AbstractFilter.java
new file mode 100644
index 0000000000..1ee1ac0f38
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/AbstractFilter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import org.alfresco.repo.audit.AuditModel;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Attribute;
+import org.dom4j.Element;
+
+/**
+ * The base class for filtering.
+ *
+ * This supports negating the filter, ie NOT.
+ *
+ * @author Andy Hind
+ */
+public abstract class AbstractFilter implements XMLModelElement
+{
+ private static Log s_logger = LogFactory.getLog(AbstractFilter.class);
+
+ private boolean invert = false;
+
+ public AbstractFilter()
+ {
+ super();
+ }
+
+ public static AbstractFilter createFilter(Element filterElement, NamespacePrefixResolver namespacePrefixResolver)
+ {
+ AbstractFilter filter;
+
+ Attribute typeAttribute = filterElement.attribute(AuditModel.AT_TYPE);
+ if (typeAttribute == null)
+ {
+ throw new AuditModelException("A filter must specify it concrete type using xsi:type");
+ }
+ if (typeAttribute.getStringValue().endsWith("FilterSet"))
+ {
+ filter = new FilterSet();
+ }
+ else if (typeAttribute.getStringValue().endsWith("KeyFilter"))
+ {
+ filter = new KeyFilter();
+ }
+ else if (typeAttribute.getStringValue().endsWith("ParameterFilter"))
+ {
+ filter = new ParameterFilter();
+ }
+ else
+ {
+ throw new AuditModelException(
+ "Invalid filter type. It must be one of: FilterSet, KeyFilter, ParameterFilter ");
+ }
+
+ filter.configure(filterElement, namespacePrefixResolver);
+ return filter;
+ }
+
+ public void configure(Element element, NamespacePrefixResolver namespacePrefixResolver)
+ {
+ Attribute invertAttribute = element.attribute(AuditModel.AT_INVERT);
+ if (invertAttribute != null)
+ {
+ invert = Boolean.valueOf(invertAttribute.getStringValue()).booleanValue();
+ }
+ else
+ {
+ invert = false;
+ }
+ }
+
+ /* package */boolean isInvert()
+ {
+ return invert;
+ }
+}
diff --git a/source/java/org/alfresco/repo/audit/model/AbstractNamedAuditEntry.java b/source/java/org/alfresco/repo/audit/model/AbstractNamedAuditEntry.java
new file mode 100644
index 0000000000..aa200227ab
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/AbstractNamedAuditEntry.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import org.alfresco.repo.audit.AuditModel;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Attribute;
+import org.dom4j.Element;
+
+public abstract class AbstractNamedAuditEntry extends AbstractAuditEntry
+{
+ private static Log s_logger = LogFactory.getLog(AbstractNamedAuditEntry.class);
+
+ private String name;
+
+ public AbstractNamedAuditEntry()
+ {
+ super();
+ }
+
+ @Override
+ void configure(AbstractAuditEntry parent, Element element, NamespacePrefixResolver namespacePrefixResolver)
+ {
+ Attribute nameAttribute = element.attribute(AuditModel.AT_NAME);
+ if (nameAttribute != null)
+ {
+ name = nameAttribute.getStringValue();
+ }
+ else
+ {
+ throw new AuditModelException("The name attribute is mandatory");
+ }
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Name = "+name);
+ }
+
+ super.configure(parent, element, namespacePrefixResolver);
+
+ }
+
+ /* package */String getName()
+ {
+ return name;
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/ApplicationAuditEntry.java b/source/java/org/alfresco/repo/audit/model/ApplicationAuditEntry.java
new file mode 100644
index 0000000000..fb67f825b2
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/ApplicationAuditEntry.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import org.alfresco.repo.audit.ApplicationAuditModel;
+import org.alfresco.repo.audit.AuditMode;
+import org.alfresco.repo.audit.RecordOptions;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class ApplicationAuditEntry extends AbstractNamedAuditEntry implements ApplicationAuditModel
+{
+ private static Log s_logger = LogFactory.getLog(ApplicationAuditEntry.class);
+
+ public ApplicationAuditEntry()
+ {
+ super();
+ }
+
+ public AuditMode beforeExecution(AuditMode auditMode, String application, String description, NodeRef key, Object... args)
+ {
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Evaluating if application is audited ..."+application);
+ }
+ return getEffectiveAuditMode();
+ }
+
+ public AuditMode afterExecution(AuditMode auditMode, String application, String description, NodeRef key, Object... args)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public AuditMode onError(AuditMode auditMode, String application, String description, NodeRef key, Object... args)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public RecordOptions getAuditRecordOptions(String application)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/AuditEntry.java b/source/java/org/alfresco/repo/audit/model/AuditEntry.java
new file mode 100644
index 0000000000..68678d200c
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/AuditEntry.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.alfresco.repo.audit.AuditConfiguration;
+import org.alfresco.repo.audit.AuditMode;
+import org.alfresco.repo.audit.AuditModel;
+import org.alfresco.repo.audit.RecordOptions;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.springframework.beans.factory.InitializingBean;
+
+public class AuditEntry extends AbstractAuditEntry implements InitializingBean, AuditModel
+{
+ private static Log s_logger = LogFactory.getLog(AuditEntry.class);
+
+ private Map services = new HashMap();
+
+ private Map applications = new HashMap();
+
+ private AuditConfiguration auditConfiguration;
+
+ private NamespacePrefixResolver namespacePrefixResolver;
+
+ public AuditEntry()
+ {
+ super();
+ }
+
+ public AuditConfiguration getAuditConfiguration()
+ {
+ return auditConfiguration;
+ }
+
+ public void setAuditConfiguration(AuditConfiguration auditConfiguration)
+ {
+ this.auditConfiguration = auditConfiguration;
+ }
+
+ public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver)
+ {
+ this.namespacePrefixResolver = namespacePrefixResolver;
+ }
+
+ public void afterPropertiesSet() throws Exception
+ {
+ Document document = createDocument();
+ Element root = document.getRootElement();
+ // Check it is the correct thing
+ configure(null, root, namespacePrefixResolver);
+ }
+
+
+
+ @Override
+ void configure(AbstractAuditEntry parent, Element element, NamespacePrefixResolver namespacePrefixResolver)
+ {
+ if (!element.getNamespaceURI().equals(AuditModel.NAME_SPACE))
+ {
+ throw new AuditModelException("Audit model has incorrect name space");
+ }
+ if (!element.getName().equals(AuditModel.EL_AUDIT))
+ {
+ throw new AuditModelException("Audit model has incorrect root node");
+ }
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Audit configuration");
+ }
+ super.configure(parent, element, namespacePrefixResolver);
+
+ // Add services
+
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Adding services ...");
+ }
+ for (Iterator nsit = element.elementIterator(AuditModel.EL_SERVICE); nsit.hasNext(); /**/)
+ {
+ Element serviceElement = (Element) nsit.next();
+ ServiceAuditEntry service = new ServiceAuditEntry();
+ service.configure(this, serviceElement, namespacePrefixResolver);
+ services.put(service.getName(), service);
+ }
+
+ // Add Applications
+
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Adding applications ...");
+ }
+ for (Iterator nsit = element.elementIterator(AuditModel.EL_APPLICATION); nsit.hasNext(); /**/)
+ {
+ Element applicationElement = (Element) nsit.next();
+ ApplicationAuditEntry application = new ApplicationAuditEntry();
+ application.configure(this, applicationElement, namespacePrefixResolver);
+ applications.put(application.getName(), application);
+ }
+ }
+
+ public AuditMode beforeExecution(AuditMode auditMode, MethodInvocation mi)
+ {
+ String serviceName = getPublicServiceIdentifier().getPublicServiceName(mi);
+ ServiceAuditEntry service = services.get(serviceName);
+ if(service != null)
+ {
+ return service.beforeExecution(auditMode, mi);
+ }
+ else
+ {
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("No specific audit entry for service "+serviceName);
+ }
+ return getEffectiveAuditMode();
+
+ }
+ }
+
+ public AuditMode afterExecution(AuditMode auditMode, MethodInvocation mi)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public RecordOptions getAuditRecordOptions(MethodInvocation mi)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public AuditMode onError(AuditMode auditMode, MethodInvocation mi)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ private Document createDocument()
+ {
+ InputStream is = auditConfiguration.getInputStream();
+ if (is == null)
+ {
+ throw new AuditModelException("Audit configuration could not be opened");
+ }
+ SAXReader reader = new SAXReader();
+ try
+ {
+ Document document = reader.read(is);
+ is.close();
+ return document;
+ }
+ catch (DocumentException e)
+ {
+ throw new AuditModelException("Failed to create audit model document ", e);
+ }
+ catch (IOException e)
+ {
+ throw new AuditModelException("Failed to close audit model document ", e);
+ }
+
+ }
+
+ public AuditMode beforeExecution(AuditMode auditMode, String application, String description, NodeRef key, Object... args)
+ {
+ ApplicationAuditEntry aae = applications.get(application);
+ if(aae != null)
+ {
+ return aae.beforeExecution(auditMode, application, description, key, args);
+ }
+ else
+ {
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("No specific audit entry for application "+application);
+ }
+ return getEffectiveAuditMode();
+
+ }
+ }
+
+ public AuditMode afterExecution(AuditMode auditMode, String application, String description, NodeRef key, Object... args)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public AuditMode onError(AuditMode auditMode, String application, String description, NodeRef key, Object... args)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public RecordOptions getAuditRecordOptions(String application)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/AuditModelException.java b/source/java/org/alfresco/repo/audit/model/AuditModelException.java
new file mode 100644
index 0000000000..7c72529429
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/AuditModelException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+
+/**
+ * Exceptions from the audit model package.
+ *
+ * @author Andy Hind
+ */
+public class AuditModelException extends AlfrescoRuntimeException
+{
+
+ /**
+ * Comment for serialVersionUID
+ */
+ private static final long serialVersionUID = -2527034441058184109L;
+
+ public AuditModelException(String msgId)
+ {
+ super(msgId);
+ }
+
+ public AuditModelException(String msgId, Object[] msgParams)
+ {
+ super(msgId, msgParams);
+ }
+
+ public AuditModelException(String msgId, Throwable cause)
+ {
+ super(msgId, cause);
+ }
+
+ public AuditModelException(String msgId, Object[] msgParams, Throwable cause)
+ {
+ super(msgId, msgParams, cause);
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/FilterSet.java b/source/java/org/alfresco/repo/audit/model/FilterSet.java
new file mode 100644
index 0000000000..d30b52fb84
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/FilterSet.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.alfresco.repo.audit.AuditModel;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Attribute;
+import org.dom4j.Element;
+
+/**
+ * This groups a set of filters together using AND or OR. They are evaluated in definition order with short cut evaluation if possible. The default beahviour is to or Filters
+ * together.
+ *
+ * @author Andy Hind
+ */
+public class FilterSet extends AbstractFilter implements XMLModelElement
+{
+ private static Log s_logger = LogFactory.getLog(FilterSet.class);
+
+ private List filters = new ArrayList();
+
+ private FilterSetMode mode = FilterSetMode.OR;
+
+ public FilterSet()
+ {
+ super();
+ }
+
+ @Override
+ public void configure(Element element, NamespacePrefixResolver namespacePrefixResolver)
+ {
+ super.configure(element, namespacePrefixResolver);
+
+ // Mode
+ Attribute modeAttribute = element.attribute(AuditModel.AT_MODE);
+ if (modeAttribute != null)
+ {
+ mode = FilterSetMode.getFilterSetMode(modeAttribute.getStringValue());
+ }
+
+ // Filters
+
+ for (Iterator nsit = element.elementIterator(AuditModel.EL_FILTER); nsit.hasNext(); /**/)
+ {
+ Element filterElement = (Element) nsit.next();
+ AbstractFilter filter = AbstractFilter.createFilter(filterElement, namespacePrefixResolver);
+ filters.add(filter);
+ }
+
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/FilterSetMode.java b/source/java/org/alfresco/repo/audit/model/FilterSetMode.java
new file mode 100644
index 0000000000..57dc90ee63
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/FilterSetMode.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+/**
+ * The enum to define if elements of a filter set are combined using AND or OR.
+ *
+ * @author Andy Hind
+ */
+public enum FilterSetMode
+{
+ AND, OR;
+
+ public static FilterSetMode getFilterSetMode(String value)
+ {
+ if(value.equalsIgnoreCase("or"))
+ {
+ return FilterSetMode.OR;
+ }
+ else if(value.equalsIgnoreCase("or"))
+ {
+ return FilterSetMode.AND;
+ }
+ else
+ {
+ throw new AuditModelException("Invalid FilterSetMode: "+value);
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/audit/model/KeyFilter.java b/source/java/org/alfresco/repo/audit/model/KeyFilter.java
new file mode 100644
index 0000000000..ddacc9f5e9
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/KeyFilter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import org.alfresco.repo.audit.AuditModel;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Attribute;
+import org.dom4j.Element;
+
+public class KeyFilter extends AbstractFilter
+{
+ private static Log s_logger = LogFactory.getLog(KeyFilter.class);
+
+ private String expression;
+
+ private KeyFilterMode keyFilterMode;
+
+ public KeyFilter()
+ {
+ super();
+ }
+
+ @Override
+ public void configure(Element element, NamespacePrefixResolver namespacePrefixResolver)
+ {
+ super.configure(element, namespacePrefixResolver);
+
+ // Filter mode
+ Attribute keyFilterTypeAttribute = element.attribute(AuditModel.AT_MODE);
+ if(keyFilterTypeAttribute != null)
+ {
+ keyFilterMode = KeyFilterMode.getKeyFilterMode(keyFilterTypeAttribute.getStringValue());
+ }
+ else
+ {
+ keyFilterMode = KeyFilterMode.ALL;
+ }
+
+ // Expression
+
+ Element expressionElement = element.element(AuditModel.EL_EXPRESSION);
+ if(expressionElement == null)
+ {
+ throw new AuditModelException("An expression is mandatory for a key filter");
+ }
+ else
+ {
+ expression = expressionElement.getText();
+ }
+ }
+
+
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/KeyFilterMode.java b/source/java/org/alfresco/repo/audit/model/KeyFilterMode.java
new file mode 100644
index 0000000000..23ef6d6e39
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/KeyFilterMode.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+/**
+ * This enum defines the type of restriction to apply to filter based on the key node ref.
+ *
+ * This restriction can be based upon:
+ *
+ *
+ *
The path to the node
+ *
The type of the node
+ *
The presence of an aspect
+ *
The NodeRef of the node
+ *
An XPATH expression evaluated in the context of the node with the return tested for the node.
+ * e.g. ".[@cm:content = 'woof']"
+ *
A simple value for equality tests given a non node argument
+ *
The protocol of the store containing the node
+ *
The identifier of the store containing the node
+ *
Or no restriction
+ *
+ *
+ * @author Andy Hind
+ */
+public enum KeyFilterMode
+{
+ PATH, TYPE, ASPECT, NODE_REF, ALL, XPATH, VALUE, STORE_PROTOCOL, STORE_IDENTIFIER;
+
+ public static KeyFilterMode getKeyFilterMode(String value)
+ {
+ if(value.equalsIgnoreCase("path"))
+ {
+ return KeyFilterMode.PATH;
+ }
+ else if(value.equalsIgnoreCase("type"))
+ {
+ return KeyFilterMode.TYPE;
+ }
+ else if(value.equalsIgnoreCase("aspect"))
+ {
+ return KeyFilterMode.ASPECT;
+ }
+ else if(value.equalsIgnoreCase("node_ref"))
+ {
+ return KeyFilterMode.NODE_REF;
+ }
+ else if(value.equalsIgnoreCase("all"))
+ {
+ return KeyFilterMode.ALL;
+ }
+ else if(value.equalsIgnoreCase("xpath"))
+ {
+ return KeyFilterMode.XPATH;
+ }
+ else if(value.equalsIgnoreCase("value"))
+ {
+ return KeyFilterMode.VALUE;
+ }
+ else if(value.equalsIgnoreCase("store_protocol"))
+ {
+ return KeyFilterMode.STORE_PROTOCOL;
+ }
+ else if(value.equalsIgnoreCase("store_identifier"))
+ {
+ return KeyFilterMode.STORE_IDENTIFIER;
+ }
+ else
+ {
+ throw new AuditModelException("Unknown KeyFilterMode: "+value);
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java b/source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java
new file mode 100644
index 0000000000..a59d6b7a72
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import org.alfresco.repo.audit.AuditMode;
+import org.alfresco.repo.audit.MethodAuditModel;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class MethodAuditEntry extends AbstractNamedAuditEntry implements MethodAuditModel
+{
+ private static Log s_logger = LogFactory.getLog(MethodAuditEntry.class);
+
+ public MethodAuditEntry()
+ {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public AuditMode beforeExecution(AuditMode auditMode, MethodInvocation mi)
+ {
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Evaluating if method is audited ..."+((ServiceAuditEntry)getParent()).getName()+"."+getName());
+ }
+ return getEffectiveAuditMode();
+ }
+
+ public AuditMode afterExecution(AuditMode auditMode, MethodInvocation mi)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public AuditMode onError(AuditMode auditMode, MethodInvocation mi)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public RecordOptionsImpl getAuditRecordOptions(MethodInvocation mi)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/ParameterFilter.java b/source/java/org/alfresco/repo/audit/model/ParameterFilter.java
new file mode 100644
index 0000000000..6bfe56d70a
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/ParameterFilter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import org.alfresco.repo.audit.AuditModel;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.alfresco.service.namespace.QName;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Element;
+
+public class ParameterFilter extends KeyFilter implements XMLModelElement
+{
+ private static Log s_logger = LogFactory.getLog(ParameterFilter.class);
+
+ private QName parameterName;
+
+ public ParameterFilter()
+ {
+ super();
+ }
+
+ @Override
+ public void configure(Element element, NamespacePrefixResolver namespacePrefixResolver)
+ {
+ super.configure(element, namespacePrefixResolver);
+
+ Element parameterNameElement = element.element(AuditModel.EL_PARAMETER_NAME);
+ if(parameterNameElement == null)
+ {
+ throw new AuditModelException("A parameter is mandatory for a parameter filter");
+ }
+ else
+ {
+ String stringQName = parameterNameElement.getStringValue();
+ if (stringQName.charAt(1) == '{')
+ {
+ parameterName = QName.createQName(stringQName);
+ }
+ else
+ {
+ parameterName = QName.createQName(stringQName);
+ }
+ }
+ }
+
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/RecordOptionsImpl.java b/source/java/org/alfresco/repo/audit/model/RecordOptionsImpl.java
new file mode 100644
index 0000000000..5b9de6ec7a
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/RecordOptionsImpl.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import org.alfresco.repo.audit.AuditModel;
+import org.alfresco.repo.audit.RecordOptions;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Element;
+
+public class RecordOptionsImpl implements XMLModelElement, RecordOptions
+{
+ private static Log s_logger = LogFactory.getLog(RecordOptionsImpl.class);
+
+ private TrueFalseUnset recordPath = TrueFalseUnset.UNSET;
+
+ private TrueFalseUnset recordFilters = TrueFalseUnset.UNSET;
+
+ private TrueFalseUnset recordSerializedReturnValue = TrueFalseUnset.UNSET;
+
+ private TrueFalseUnset recordSerializedExceptions = TrueFalseUnset.UNSET;
+
+ private TrueFalseUnset recordSerializedMethodArguments = TrueFalseUnset.UNSET;
+
+ private TrueFalseUnset recordSerializedKeyPropertiesBeforeEvaluation = TrueFalseUnset.UNSET;
+
+ private TrueFalseUnset recordSerializedKeyPropertiesAfterEvaluation = TrueFalseUnset.UNSET;
+
+ public RecordOptionsImpl()
+ {
+ super();
+ }
+
+ public static RecordOptionsImpl mergeRecordOptions(RecordOptions primary, RecordOptions secondary)
+ {
+ RecordOptionsImpl answer = new RecordOptionsImpl();
+ setOptions(answer, primary, true);
+ setOptions(answer, secondary, false);
+ return answer;
+ }
+
+ private static void setOptions(RecordOptionsImpl on, RecordOptions from, boolean force)
+ {
+ if(force || on.recordFilters.equals( TrueFalseUnset.UNSET))
+ {
+ on.recordFilters = from.getRecordFilters();
+ }
+ if(force || on.recordPath.equals( TrueFalseUnset.UNSET))
+ {
+ on.recordPath = from.getRecordPath();
+ }
+ if(force || on.recordSerializedExceptions.equals( TrueFalseUnset.UNSET))
+ {
+ on.recordSerializedExceptions = from.getRecordSerializedExceptions();
+ }
+ if(force || on.recordSerializedKeyPropertiesAfterEvaluation.equals( TrueFalseUnset.UNSET))
+ {
+ on.recordSerializedKeyPropertiesAfterEvaluation = from.getRecordSerializedKeyPropertiesAfterEvaluation();
+ }
+ if(force || on.recordSerializedKeyPropertiesBeforeEvaluation.equals( TrueFalseUnset.UNSET))
+ {
+ on.recordSerializedKeyPropertiesBeforeEvaluation = from.getRecordSerializedKeyPropertiesBeforeEvaluation();
+ }
+ if(force || on.recordSerializedMethodArguments.equals( TrueFalseUnset.UNSET))
+ {
+ on.recordSerializedMethodArguments = from.getRecordSerializedMethodArguments();
+ }
+ if(force || on.recordSerializedReturnValue.equals( TrueFalseUnset.UNSET))
+ {
+ on.recordSerializedReturnValue = from.getRecordSerializedReturnValue();
+ }
+ }
+
+ public TrueFalseUnset getRecordFilters()
+ {
+ return recordFilters;
+ }
+
+ public TrueFalseUnset getRecordPath()
+ {
+ return recordPath;
+ }
+
+ public TrueFalseUnset getRecordSerializedExceptions()
+ {
+ return recordSerializedExceptions;
+ }
+
+ public TrueFalseUnset getRecordSerializedKeyPropertiesAfterEvaluation()
+ {
+ return recordSerializedKeyPropertiesAfterEvaluation;
+ }
+
+ public TrueFalseUnset getRecordSerializedKeyPropertiesBeforeEvaluation()
+ {
+ return recordSerializedKeyPropertiesBeforeEvaluation;
+ }
+
+ public TrueFalseUnset getRecordSerializedMethodArguments()
+ {
+ return recordSerializedMethodArguments;
+ }
+
+ public TrueFalseUnset getRecordSerializedReturnValue()
+ {
+ return recordSerializedReturnValue;
+ }
+
+ public void configure(Element recordOptionElement, NamespacePrefixResolver namespacePrefixResolver)
+ {
+ Element recordFiltersElement = recordOptionElement.element(AuditModel.EL_RECORD_FILTERS);
+ if (recordFiltersElement != null)
+ {
+ recordFilters = TrueFalseUnset.getTrueFalseUnset(recordFiltersElement.getStringValue());
+ }
+
+ Element recordPathElement = recordOptionElement.element(AuditModel.EL_RECORD_PATH);
+ if (recordPathElement != null)
+ {
+ recordPath = TrueFalseUnset.getTrueFalseUnset(recordPathElement.getStringValue());
+ }
+
+ Element recordSerAgrsElement = recordOptionElement.element(AuditModel.EL_RECORD_SER_ARGS);
+ if (recordSerAgrsElement != null)
+ {
+ recordSerializedMethodArguments = TrueFalseUnset.getTrueFalseUnset(recordSerAgrsElement.getStringValue());
+ }
+
+ Element recordSerExElement = recordOptionElement.element(AuditModel.EL_RECORD_SER_EX);
+ if (recordSerExElement != null)
+ {
+ recordSerializedExceptions = TrueFalseUnset.getTrueFalseUnset(recordSerExElement.getStringValue());
+ }
+
+ Element recordSerPropAfterElement = recordOptionElement.element(AuditModel.EL_RECORD_SER_PROP_AFTER);
+ if (recordSerPropAfterElement != null)
+ {
+ recordSerializedKeyPropertiesAfterEvaluation = TrueFalseUnset.getTrueFalseUnset(recordSerPropAfterElement
+ .getStringValue());
+ }
+
+ Element recordSerPropBeforeElement = recordOptionElement.element(AuditModel.EL_RECORD_SER_PROP_BEFORE);
+ if (recordSerPropBeforeElement != null)
+ {
+ recordSerializedKeyPropertiesBeforeEvaluation = TrueFalseUnset.getTrueFalseUnset(recordSerPropBeforeElement
+ .getStringValue());
+ }
+
+ Element recordSerRetElement = recordOptionElement.element(AuditModel.EL_RECORD_SER_RETURN_VAL);
+ if (recordSerRetElement != null)
+ {
+ recordSerializedReturnValue = TrueFalseUnset.getTrueFalseUnset(recordSerRetElement.getStringValue());
+ }
+
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Record Options(");
+ builder.append("Filters=").append(getRecordFilters());
+ builder.append(",Path=").append(getRecordPath());
+ builder.append(",Exception=").append(getRecordSerializedExceptions());
+ builder.append(",PropertiesBefore=").append(getRecordSerializedKeyPropertiesAfterEvaluation());
+ builder.append(",PropertiesAfter=").append(getRecordSerializedKeyPropertiesBeforeEvaluation());
+ builder.append(",Args=").append(getRecordSerializedMethodArguments());
+ builder.append(",Return=").append(getRecordSerializedReturnValue());
+ builder.append(")");
+ return builder.toString();
+ }
+
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java b/source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java
new file mode 100644
index 0000000000..764f004044
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.alfresco.repo.audit.AuditMode;
+import org.alfresco.repo.audit.AuditModel;
+import org.alfresco.repo.audit.MethodAuditModel;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.dom4j.Element;
+
+public class ServiceAuditEntry extends AbstractNamedAuditEntry implements MethodAuditModel
+{
+ private static Log s_logger = LogFactory.getLog(ServiceAuditEntry.class);
+
+ private Map methods = new HashMap();
+
+ public ServiceAuditEntry()
+ {
+ super();
+ }
+
+ @Override
+ void configure(AbstractAuditEntry parent, Element element, NamespacePrefixResolver namespacePrefixResolver)
+ {
+ super.configure(parent, element, namespacePrefixResolver);
+
+ // Add Methods
+
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Adding methods to service "+getName());
+ }
+ for (Iterator nsit = element.elementIterator(AuditModel.EL_METHOD); nsit.hasNext(); /**/)
+ {
+ Element methodElement = (Element) nsit.next();
+ MethodAuditEntry method = new MethodAuditEntry();
+ method.configure(this, methodElement, namespacePrefixResolver);
+ methods.put(method.getName(), method);
+ }
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("...added methods for service "+getName());
+ }
+ }
+
+ public AuditMode beforeExecution(AuditMode auditMode, MethodInvocation mi)
+ {
+ String methodName = mi.getMethod().getName();
+ MethodAuditEntry method = methods.get(methodName);
+ if (method != null)
+ {
+ return method.beforeExecution(auditMode, mi);
+ }
+ else
+ {
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("Evaluating if service is audited (no specific setting) for "+getName()+"."+methodName);
+ }
+ return getEffectiveAuditMode();
+ }
+ }
+
+ public AuditMode afterExecution(AuditMode auditMode, MethodInvocation mi)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public AuditMode onError(AuditMode auditMode, MethodInvocation mi)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public RecordOptionsImpl getAuditRecordOptions(MethodInvocation mi)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/audit/model/TrueFalseUnset.java b/source/java/org/alfresco/repo/audit/model/TrueFalseUnset.java
new file mode 100644
index 0000000000..6f2eb54368
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/TrueFalseUnset.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+/**
+ * An enum for the values
+ *
+ *
TRUE
+ *
FALSE
+ *
UNSET
+ *
+ *
+ * @author Andy Hind
+ */
+public enum TrueFalseUnset
+{
+ TRUE, FALSE, UNSET;
+
+ public static TrueFalseUnset getTrueFalseUnset(String value)
+ {
+ if(value.equalsIgnoreCase("true"))
+ {
+ return TrueFalseUnset.TRUE;
+ }
+ else if(value.equalsIgnoreCase("false"))
+ {
+ return TrueFalseUnset.FALSE;
+ }
+ else if(value.equalsIgnoreCase("unset"))
+ {
+ return TrueFalseUnset.UNSET;
+ }
+ else
+ {
+ throw new AuditModelException("Invalid value for TrueFalseUnset: "+value);
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/audit/model/XMLModelElement.java b/source/java/org/alfresco/repo/audit/model/XMLModelElement.java
new file mode 100644
index 0000000000..11c3293232
--- /dev/null
+++ b/source/java/org/alfresco/repo/audit/model/XMLModelElement.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.audit.model;
+
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.dom4j.Element;
+
+public interface XMLModelElement
+{
+ void configure(Element element, NamespacePrefixResolver namespacePrefixResolver );
+}
diff --git a/source/java/org/alfresco/repo/cache/EhCacheAdapter.java b/source/java/org/alfresco/repo/cache/EhCacheAdapter.java
index 95fdb60f6c..46f01e3bbd 100644
--- a/source/java/org/alfresco/repo/cache/EhCacheAdapter.java
+++ b/source/java/org/alfresco/repo/cache/EhCacheAdapter.java
@@ -100,13 +100,6 @@ public class EhCacheAdapter
public void clear()
{
- try
- {
- cache.removeAll();
- }
- catch (IOException e)
- {
- throw new AlfrescoRuntimeException("Failed to clear cache", e);
- }
+ cache.removeAll();
}
}
diff --git a/source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java b/source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java
new file mode 100644
index 0000000000..f3dc89a82a
--- /dev/null
+++ b/source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.cache;
+
+import java.io.IOException;
+
+import net.sf.ehcache.CacheException;
+import net.sf.ehcache.CacheManager;
+
+import org.alfresco.util.PropertyCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.io.Resource;
+
+/**
+ * This is virtually a copy of the Springframework version, with the exception
+ * that it uses the newer constructors for the EHCacheManager
+ * instances.
+ *
+ * @author Derek Hulley
+ */
+public class EhCacheManagerFactoryBean implements FactoryBean, InitializingBean, DisposableBean
+{
+ protected final Log logger = LogFactory.getLog(EhCacheManagerFactoryBean.class);
+
+ private Resource configLocation;
+ private CacheManager cacheManager;
+
+ /**
+ *
+ * @param configLocation a resource location using the file: or classpath: prefix
+ */
+ public void setConfigLocation(Resource configLocation)
+ {
+ this.configLocation = configLocation;
+ }
+
+ public void afterPropertiesSet() throws IOException, CacheException
+ {
+ PropertyCheck.mandatory(this, "configLocation", configLocation);
+
+ logger.info("Initializing EHCache CacheManager");
+ this.cacheManager = new CacheManager(this.configLocation.getURL());
+ }
+
+ public Object getObject()
+ {
+ return this.cacheManager;
+ }
+
+ public Class getObjectType()
+ {
+ return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class);
+ }
+
+ public boolean isSingleton()
+ {
+ return true;
+ }
+
+ public void destroy()
+ {
+ logger.info("Shutting down EHCache CacheManager");
+ this.cacheManager.shutdown();
+ }
+}
diff --git a/source/java/org/alfresco/repo/cache/TransactionalCache.java b/source/java/org/alfresco/repo/cache/TransactionalCache.java
index 3348d3f097..f2080be2b4 100644
--- a/source/java/org/alfresco/repo/cache/TransactionalCache.java
+++ b/source/java/org/alfresco/repo/cache/TransactionalCache.java
@@ -16,7 +16,6 @@
*/
package org.alfresco.repo.cache;
-import java.io.IOException;
import java.io.Serializable;
import java.util.List;
@@ -438,15 +437,8 @@ public class TransactionalCache
// and also serves to ensure that the shared cache will be ignored
// for the remainder of the transaction
txnData.isClearOn = true;
- try
- {
- txnData.updatedItemsCache.removeAll();
- txnData.removedItemsCache.removeAll();
- }
- catch (IOException e)
- {
- throw new AlfrescoRuntimeException("Failed to clear caches", e);
- }
+ txnData.updatedItemsCache.removeAll();
+ txnData.removedItemsCache.removeAll();
}
else // no transaction
{
diff --git a/source/java/org/alfresco/repo/content/metadata/MailMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/MailMetadataExtracter.java
index 73fae3b2e7..15e27d8478 100644
--- a/source/java/org/alfresco/repo/content/metadata/MailMetadataExtracter.java
+++ b/source/java/org/alfresco/repo/content/metadata/MailMetadataExtracter.java
@@ -29,7 +29,6 @@ import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
-import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.poi.poifs.eventfilesystem.POIFSReader;
import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
@@ -130,7 +129,8 @@ public class MailMetadataExtracter extends AbstractMetadataExtracter
private static final String ENCODING_UNICODE = "001F";
private static final String SUBSTG_MESSAGEBODY = "1000";
- private static final String SUBSTG_RECIPIENTEMAIL = "39FE";
+ private static final String SUBSTG_RECIPIENTEMAIL = "39FE"; // 7bit email address
+ private static final String SUBSTG_RECIPIENTSEARCH = "300B"; // address 'search' variant
private static final String SUBSTG_RECEIVEDEMAIL = "0076";
private static final String SUBSTG_SENDEREMAIL = "0C1F";
private static final String SUBSTG_DATE = "0047";
@@ -159,6 +159,27 @@ public class MailMetadataExtracter extends AbstractMetadataExtracter
{
receipientEmails.get().add(convertExchangeAddress(extractText()));
}
+ else if (type.equals(SUBSTG_RECIPIENTSEARCH))
+ {
+ String email = extractText(ENCODING_TEXT);
+ int smptIndex = email.indexOf("SMTP:");
+ if (smptIndex != -1)
+ {
+ /* also may be used for SUBSTG_RECIPIENTTRANSPORT = "5FF7";
+ with search for SMPT followed by a null char */
+
+ // this is a secondary mechanism for encoding a receipient email address
+ // the 7 bit email address may not have been set by Outlook - so this is needed instead
+ // handle null character at end of string
+ int endIndex = email.length();
+ if (email.codePointAt(email.length() - 1) == 0)
+ {
+ endIndex--;
+ }
+ email = email.substring(smptIndex + 5, endIndex);
+ receipientEmails.get().add(email);
+ }
+ }
else if (type.equals(SUBSTG_RECEIVEDEMAIL))
{
destination.put(ContentModel.PROP_ADDRESSEE, convertExchangeAddress(extractText()));
@@ -169,8 +190,8 @@ public class MailMetadataExtracter extends AbstractMetadataExtracter
}
else if (type.equals(SUBSTG_DATE))
{
- // the date is not really plain text - but it's easier to parse as such
- String date = extractText();
+ // the date is not "really" plain text - but it's appropriate to parse as such
+ String date = extractText(ENCODING_TEXT);
int valueIndex = date.indexOf("l=");
if (valueIndex != -1)
{
@@ -203,15 +224,28 @@ public class MailMetadataExtracter extends AbstractMetadataExtracter
*/
private String extractText()
throws IOException
+ {
+ return extractText(this.encoding);
+ }
+
+ /**
+ * Extract the text from the stream based on the encoding
+ *
+ * @return String
+ *
+ * @throws IOException
+ */
+ private String extractText(String encoding)
+ throws IOException
{
byte[] data = new byte[stream.available()];
stream.read(data);
- if (this.encoding.equals(ENCODING_TEXT) || this.encoding.equals(ENCODING_BINARY))
+ if (encoding.equals(ENCODING_TEXT) || encoding.equals(ENCODING_BINARY))
{
return new String(data);
}
- else if (this.encoding.equals(ENCODING_UNICODE))
+ else if (encoding.equals(ENCODING_UNICODE))
{
// convert double-byte encoding to single byte for String conversion
byte[] b = new byte[data.length >> 1];
diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java
index f0e1b7be09..8172f86235 100644
--- a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java
+++ b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java
@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.policy.ClassPolicyDelegate;
import org.alfresco.repo.policy.JavaBehaviour;
@@ -31,6 +32,7 @@ import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.policy.PolicyScope;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
+import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
@@ -401,6 +403,23 @@ public class CopyServiceImpl implements CopyService
}
}
+ // if the parent node is the same, then remove the name property - it will have to
+ // be changed by the client code
+ AssociationDefinition assocDef = dictionaryService.getAssociation(destinationAssocTypeQName);
+ if (!assocDef.isChild())
+ {
+ throw new AlfrescoRuntimeException("Association is not a child association: " + destinationAssocTypeQName);
+ }
+ else
+ {
+ ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef;
+ if (!childAssocDef.getDuplicateChildNamesAllowed())
+ {
+ // duplicate children are not allowed.
+ properties.remove(ContentModel.PROP_NAME);
+ }
+ }
+
// Create the new node
ChildAssociationRef destinationChildAssocRef = this.nodeService.createNode(
destinationParent,
diff --git a/source/java/org/alfresco/repo/domain/NodeStatus.java b/source/java/org/alfresco/repo/domain/NodeStatus.java
index 4df06d8cf1..53969c0a69 100644
--- a/source/java/org/alfresco/repo/domain/NodeStatus.java
+++ b/source/java/org/alfresco/repo/domain/NodeStatus.java
@@ -41,9 +41,9 @@ public interface NodeStatus
public void setNode(Node node);
- public String getChangeTxnId();
+ public Transaction getTransaction();
- public void setChangeTxnId(String txnId);
+ public void setTransaction(Transaction transaction);
public boolean isDeleted();
}
diff --git a/source/java/org/alfresco/repo/domain/Server.java b/source/java/org/alfresco/repo/domain/Server.java
new file mode 100644
index 0000000000..f07c70b423
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/Server.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.domain;
+
+/**
+ * Interface for persistent server objects. These persist
+ * details of the servers that have committed transactions to the
+ * database, for instance.
+ *
+ * @author Derek Hulley
+ */
+public interface Server
+{
+ public Long getId();
+
+ public String getIpAddress();
+
+ public void setIpAddress(String ipAddress);
+}
diff --git a/source/java/org/alfresco/repo/domain/Transaction.java b/source/java/org/alfresco/repo/domain/Transaction.java
new file mode 100644
index 0000000000..42633a70e7
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/Transaction.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.domain;
+
+/**
+ * Interface for persistent transaction objects.
+ *
+ * @author Derek Hulley
+ */
+public interface Transaction
+{
+ public Long getId();
+
+ public String getChangeTxnId();
+
+ public void setChangeTxnId(String changeTxnId);
+
+ public Server getServer();
+
+ public void setServer(Server server);
+}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java
index 47014f7587..fc9f9535ba 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java
@@ -31,8 +31,10 @@ import org.alfresco.repo.domain.Node;
import org.alfresco.repo.domain.NodeKey;
import org.alfresco.repo.domain.NodeStatus;
import org.alfresco.repo.domain.PropertyValue;
+import org.alfresco.repo.domain.Server;
import org.alfresco.repo.domain.Store;
import org.alfresco.repo.domain.StoreKey;
+import org.alfresco.repo.domain.Transaction;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.StoreRef;
@@ -53,8 +55,11 @@ import org.hibernate.exception.ConstraintViolationException;
public class HibernateNodeTest extends BaseSpringTest
{
private static final String TEST_NAMESPACE = "http://www.alfresco.org/test/HibernateNodeTest";
+ private static int i = 0;
private Store store;
+ private Server server;
+ private Transaction transaction;
public HibernateNodeTest()
{
@@ -68,6 +73,18 @@ public class HibernateNodeTest extends BaseSpringTest
store.setKey(storeKey);
// persist so that it is present in the hibernate cache
getSession().save(store);
+
+ server = (Server) getSession().get(ServerImpl.class, new Long(1));
+ if (server == null)
+ {
+ server = new ServerImpl();
+ server.setIpAddress("" + "i_" + System.currentTimeMillis());
+ getSession().save(server);
+ }
+ transaction = new TransactionImpl();
+ transaction.setServer(server);
+ transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
+ getSession().save(transaction);
}
protected void onTearDownInTransaction()
@@ -108,7 +125,7 @@ public class HibernateNodeTest extends BaseSpringTest
// create the node status
NodeStatus nodeStatus = new NodeStatusImpl();
nodeStatus.setKey(key);
- nodeStatus.setChangeTxnId("txn:123");
+ nodeStatus.setTransaction(transaction);
getSession().save(nodeStatus);
// create a new Node
@@ -131,7 +148,7 @@ public class HibernateNodeTest extends BaseSpringTest
node = nodeStatus.getNode();
assertNotNull("Node was not attached to status", node);
// change the values
- nodeStatus.setChangeTxnId("txn:456");
+ transaction.setChangeTxnId("txn:456");
// delete the node
getSession().delete(node);
@@ -351,7 +368,7 @@ public class HibernateNodeTest extends BaseSpringTest
NodeStatus containerNodeStatus = new NodeStatusImpl();
containerNodeStatus.setKey(containerNodeKey);
containerNodeStatus.setNode(containerNode);
- containerNodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
+ containerNodeStatus.setTransaction(transaction);
getSession().save(containerNodeStatus);
// make content node 1
Node contentNode1 = new NodeImpl();
@@ -366,7 +383,7 @@ public class HibernateNodeTest extends BaseSpringTest
NodeStatus contentNodeStatus1 = new NodeStatusImpl();
contentNodeStatus1.setKey(contentNodeKey1);
contentNodeStatus1.setNode(contentNode1);
- contentNodeStatus1.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
+ contentNodeStatus1.setTransaction(transaction);
getSession().save(contentNodeStatus1);
// make content node 2
Node contentNode2 = new NodeImpl();
@@ -381,7 +398,7 @@ public class HibernateNodeTest extends BaseSpringTest
NodeStatus contentNodeStatus2 = new NodeStatusImpl();
contentNodeStatus2.setKey(contentNodeKey2);
contentNodeStatus2.setNode(contentNode2);
- contentNodeStatus2.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
+ contentNodeStatus2.setTransaction(transaction);
getSession().save(contentNodeStatus2);
// create an association to content 1
ChildAssoc assoc1 = new ChildAssocImpl();
diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
index 79485306d3..15fe0ed614 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
+++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml
@@ -113,6 +113,16 @@
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
@@ -309,25 +316,26 @@
select distinct
- status.changeTxnId
+ transaction.changeTxnId
from
- org.alfresco.repo.domain.hibernate.NodeStatusImpl as status
+ org.alfresco.repo.domain.hibernate.TransactionImpl as transaction
where
- status.changeTxnId > :currentTxnId
+ transaction.changeTxnId > :currentTxnId
order by
- status.changeTxnId
+ transaction.changeTxnId
select
- count(status.changeTxnId)
+ count(transaction.changeTxnId)
from
org.alfresco.repo.domain.hibernate.NodeStatusImpl as status
+ join status.transaction as transaction
where
status.key.protocol = :storeProtocol and
status.key.identifier = :storeIdentifier and
status.node.id is not null and
- status.changeTxnId = :changeTxnId
+ transaction.changeTxnId = :changeTxnId
@@ -335,11 +343,12 @@
status
from
org.alfresco.repo.domain.hibernate.NodeStatusImpl as status
+ join status.transaction as transaction
where
status.key.protocol = :storeProtocol and
status.key.identifier = :storeIdentifier and
status.node.id is not null and
- status.changeTxnId = :changeTxnId
+ transaction.changeTxnId = :changeTxnId
@@ -347,11 +356,12 @@
status
from
org.alfresco.repo.domain.hibernate.NodeStatusImpl as status
+ join status.transaction as transaction
where
status.key.protocol = :storeProtocol and
status.key.identifier = :storeIdentifier and
status.node.id is null and
- status.changeTxnId = :changeTxnId
+ transaction.changeTxnId = :changeTxnId
diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeStatusImpl.java b/source/java/org/alfresco/repo/domain/hibernate/NodeStatusImpl.java
index 77ea062ebb..e5ceca5843 100644
--- a/source/java/org/alfresco/repo/domain/hibernate/NodeStatusImpl.java
+++ b/source/java/org/alfresco/repo/domain/hibernate/NodeStatusImpl.java
@@ -21,6 +21,7 @@ import java.io.Serializable;
import org.alfresco.repo.domain.Node;
import org.alfresco.repo.domain.NodeKey;
import org.alfresco.repo.domain.NodeStatus;
+import org.alfresco.repo.domain.Transaction;
import org.alfresco.util.EqualsHelper;
/**
@@ -34,15 +35,16 @@ public class NodeStatusImpl implements NodeStatus, Serializable
private NodeKey key;
private Node node;
- private String changeTxnId;
+ private Transaction transaction;
+ @Override
public String toString()
{
StringBuilder sb = new StringBuilder(50);
sb.append("NodeStatus")
.append("[key=").append(key)
.append(", node=").append(node == null ? null : node.getNodeRef())
- .append(", txn=").append(changeTxnId)
+ .append(", txn=").append(transaction)
.append("]");
return sb.toString();
}
@@ -85,14 +87,14 @@ public class NodeStatusImpl implements NodeStatus, Serializable
this.node = node;
}
- public String getChangeTxnId()
+ public Transaction getTransaction()
{
- return changeTxnId;
+ return transaction;
}
- public void setChangeTxnId(String txnId)
+ public void setTransaction(Transaction transaction)
{
- this.changeTxnId = txnId;
+ this.transaction = transaction;
}
public boolean isDeleted()
diff --git a/source/java/org/alfresco/repo/domain/hibernate/ServerImpl.java b/source/java/org/alfresco/repo/domain/hibernate/ServerImpl.java
new file mode 100644
index 0000000000..0fab53f8a5
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/hibernate/ServerImpl.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.domain.hibernate;
+
+import java.io.Serializable;
+
+import org.alfresco.repo.domain.Server;
+
+/**
+ * Bean containing all the persistence data representing a Server.
+ *
+ * This implementation of the {@link org.alfresco.repo.domain.Service Service} interface is
+ * Hibernate specific.
+ *
+ * @author Derek Hulley
+ */
+public class ServerImpl extends LifecycleAdapter implements Server, Serializable
+{
+ private static final long serialVersionUID = 8063452519040344479L;
+
+ private Long id;
+ private String ipAddress;
+
+ public ServerImpl()
+ {
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(50);
+ sb.append("Server")
+ .append("[id=").append(id)
+ .append(", ipAddress=").append(ipAddress)
+ .append("]");
+ return sb.toString();
+ }
+
+ public Long getId()
+ {
+ return id;
+ }
+
+ /**
+ * For Hibernate use
+ */
+ @SuppressWarnings("unused")
+ private void setId(Long id)
+ {
+ this.id = id;
+ }
+
+ public String getIpAddress()
+ {
+ return ipAddress;
+ }
+
+ public void setIpAddress(String ipAddress)
+ {
+ this.ipAddress = ipAddress;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml
new file mode 100644
index 0000000000..e770f3f26c
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select
+ server
+ from
+ org.alfresco.repo.domain.hibernate.ServerImpl as server
+ where
+ server.ipAddress = :ipAddress
+
+
+
diff --git a/source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java b/source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java
new file mode 100644
index 0000000000..21a26946ea
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.domain.hibernate;
+
+import java.io.Serializable;
+
+import org.alfresco.repo.domain.Server;
+import org.alfresco.repo.domain.Transaction;
+
+/**
+ * Bean containing all the persistence data representing a Transaction.
+ *
+ * This implementation of the {@link org.alfresco.repo.domain.Transaction Transaction} interface is
+ * Hibernate specific.
+ *
+ * @author Derek Hulley
+ */
+public class TransactionImpl extends LifecycleAdapter implements Transaction, Serializable
+{
+ private static final long serialVersionUID = -8264339795578077552L;
+
+ private Long id;
+ private String changeTxnId;
+ private Server server;
+
+ public TransactionImpl()
+ {
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(50);
+ sb.append("Transaction")
+ .append("[id=").append(id)
+ .append(", changeTxnId=").append(changeTxnId)
+ .append("]");
+ return sb.toString();
+ }
+
+ public Long getId()
+ {
+ return id;
+ }
+
+ /**
+ * For Hibernate use
+ */
+ @SuppressWarnings("unused")
+ private void setId(Long id)
+ {
+ this.id = id;
+ }
+
+ public String getChangeTxnId()
+ {
+ return changeTxnId;
+ }
+
+ public void setChangeTxnId(String changeTransactionId)
+ {
+ this.changeTxnId = changeTransactionId;
+ }
+
+ public Server getServer()
+ {
+ return server;
+ }
+
+ public void setServer(Server server)
+ {
+ this.server = server;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
new file mode 100644
index 0000000000..e17a13e6ae
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
@@ -0,0 +1,529 @@
+/*
+ * Copyright (C) 2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.domain.schema;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.InputStreamReader;
+import java.io.Writer;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.i18n.I18NUtil;
+import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch;
+import org.alfresco.util.TempFileProvider;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
+import org.hibernate.tool.hbm2ddl.SchemaExport;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
+import org.springframework.util.ResourceUtils;
+
+/**
+ * Bootstraps the schema and schema update. The schema is considered missing if the applied patch table
+ * is not present, and the schema is considered empty if the applied patch table is empty.
+ *
+ * @author Derek Hulley
+ */
+public class SchemaBootstrap implements ApplicationListener
+{
+ /** The placeholder for the configured Dialect class name: ${db.script.dialect} */
+ private static final String PLACEHOLDER_SCRIPT_DIALECT = "\\$\\{db\\.script\\.dialect\\}";
+
+ private static final String MSG_EXECUTING_SCRIPT = "schema.update.msg.executing_script";
+ private static final String ERR_UPDATE_FAILED = "schema.update.err.update_failed";
+ private static final String ERR_VALIDATION_FAILED = "schema.update.err.validation_failed";
+ private static final String ERR_SCRIPT_NOT_RUN = "schema.update.err.update_script_not_run";
+ private static final String ERR_SCRIPT_NOT_FOUND = "schema.update.err.script_not_found";
+ private static final String ERR_STATEMENT_TERMINATOR = "schema.update.err.statement_terminator";
+
+ private static Log logger = LogFactory.getLog(SchemaBootstrap.class);
+
+ private LocalSessionFactoryBean localSessionFactory;
+ private String schemaOuputFilename;
+ private boolean updateSchema;
+ private List postCreateScriptUrls;
+ private List validateUpdateScriptPatches;
+ private List applyUpdateScriptPatches;
+
+ public SchemaBootstrap()
+ {
+ postCreateScriptUrls = new ArrayList(1);
+ validateUpdateScriptPatches = new ArrayList(4);
+ applyUpdateScriptPatches = new ArrayList(4);
+ }
+
+ public void setLocalSessionFactory(LocalSessionFactoryBean localSessionFactory) throws BeansException
+ {
+ this.localSessionFactory = localSessionFactory;
+ }
+
+ /**
+ * Set this to output the full database creation script
+ *
+ * @param schemaOuputFilename the name of a file to dump the schema to, or null to ignore
+ */
+ public void setSchemaOuputFilename(String schemaOuputFilename)
+ {
+ this.schemaOuputFilename = schemaOuputFilename;
+ }
+
+ /**
+ * Set whether to modify the schema or not. Either way, the schema will be validated.
+ *
+ * @param updateSchema true to update and validate the schema, otherwise false to just
+ * validate the schema. Default is true.
+ */
+ public void setUpdateSchema(boolean updateSchema)
+ {
+ this.updateSchema = updateSchema;
+ }
+
+ /**
+ * Set the scripts that must be executed after the schema has been created.
+ *
+ * @param postCreateScriptUrls file URLs
+ *
+ * @see #PLACEHOLDER_SCRIPT_DIALECT
+ */
+ public void setPostCreateScriptUrls(List postUpdateScriptUrls)
+ {
+ this.postCreateScriptUrls = postUpdateScriptUrls;
+ }
+
+ /**
+ * Set the schema script patches that must have been applied. These will not be
+ * applied to the database. These can be used where the script cannot be
+ * applied automatically or where a particular upgrade path is no longer supported.
+ * For example, at version 3.0, the upgrade scripts for version 1.4 may be considered
+ * unsupported - this doesn't prevent the manual application of the scripts, though.
+ *
+ * @param applyUpdateScriptPatches a list of schema patches to check
+ */
+ public void setValidateUpdateScriptPatches(List scriptPatches)
+ {
+ this.validateUpdateScriptPatches = scriptPatches;
+ }
+
+ /**
+ * Set the schema script patches that may be executed during an update.
+ *
+ * @param applyUpdateScriptPatches a list of schema patches to check
+ */
+ public void setApplyUpdateScriptPatches(List scriptPatches)
+ {
+ this.applyUpdateScriptPatches = scriptPatches;
+ }
+
+ public void onApplicationEvent(ApplicationEvent event)
+ {
+ if (!(event instanceof ContextRefreshedEvent))
+ {
+ // only work on startup
+ return;
+ }
+
+ // do everything in a transaction
+ Session session = getLocalSessionFactory().openSession();
+ Transaction transaction = session.beginTransaction();
+ try
+ {
+ // make sure that we don't autocommit
+ Connection connection = session.connection();
+ connection.setAutoCommit(false);
+
+ Configuration cfg = localSessionFactory.getConfiguration();
+ // dump the schema, if required
+ if (schemaOuputFilename != null)
+ {
+ File schemaOutputFile = new File(schemaOuputFilename);
+ dumpSchemaCreate(cfg, schemaOutputFile);
+ }
+
+ // update the schema, if required
+ if (updateSchema)
+ {
+ updateSchema(cfg, session, connection);
+ }
+
+ // verify that all patches have been applied correctly
+ checkSchemaPatchScripts(cfg, session, connection, validateUpdateScriptPatches, false); // check scripts
+ checkSchemaPatchScripts(cfg, session, connection, applyUpdateScriptPatches, false); // check scripts
+
+ // all done successfully
+ transaction.commit();
+ }
+ catch (Throwable e)
+ {
+ try { transaction.rollback(); } catch (Throwable ee) {}
+ if (updateSchema)
+ {
+ throw new AlfrescoRuntimeException(ERR_UPDATE_FAILED, e);
+ }
+ else
+ {
+ throw new AlfrescoRuntimeException(ERR_VALIDATION_FAILED, e);
+ }
+ }
+ }
+
+ private void dumpSchemaCreate(Configuration cfg, File schemaOutputFile)
+ {
+ // if the file exists, delete it
+ if (schemaOutputFile.exists())
+ {
+ schemaOutputFile.delete();
+ }
+ SchemaExport schemaExport = new SchemaExport(cfg)
+ .setFormat(true)
+ .setHaltOnError(true)
+ .setOutputFile(schemaOutputFile.getAbsolutePath())
+ .setDelimiter(";");
+ schemaExport.execute(false, false, false, true);
+ }
+
+ private SessionFactory getLocalSessionFactory()
+ {
+ return (SessionFactory) localSessionFactory.getObject();
+ }
+
+ /**
+ * @return Returns the number of applied patches
+ */
+ private int countAppliedPatches(Connection connection) throws Exception
+ {
+ Statement stmt = connection.createStatement();
+ try
+ {
+ ResultSet rs = stmt.executeQuery("select count(id) from alf_applied_patch");
+ rs.next();
+ int count = rs.getInt(1);
+ return count;
+ }
+ catch (Throwable e)
+ {
+ // we'll try another table name
+ }
+ finally
+ {
+ try { stmt.close(); } catch (Throwable e) {}
+ }
+ // for pre-1.4 databases, the table was named differently
+ stmt = connection.createStatement();
+ try
+ {
+ ResultSet rs = stmt.executeQuery("select count(id) from applied_patch");
+ rs.next();
+ int count = rs.getInt(1);
+ return count;
+ }
+ finally
+ {
+ try { stmt.close(); } catch (Throwable e) {}
+ }
+ }
+
+ /**
+ * @return Returns the number of applied patches
+ */
+ private boolean didPatchSucceed(Connection connection, String patchId) throws Exception
+ {
+ Statement stmt = connection.createStatement();
+ try
+ {
+ ResultSet rs = stmt.executeQuery("select succeeded from alf_applied_patch where id = '" + patchId + "'");
+ if (!rs.next())
+ {
+ return false;
+ }
+ boolean succeeded = rs.getBoolean(1);
+ return succeeded;
+ }
+ catch (Throwable e)
+ {
+ // we'll try another table name
+ }
+ finally
+ {
+ try { stmt.close(); } catch (Throwable e) {}
+ }
+ // for pre-1.4 databases, the table was named differently
+ stmt = connection.createStatement();
+ try
+ {
+ ResultSet rs = stmt.executeQuery("select succeeded from applied_patch where id = '" + patchId + "'");
+ if (!rs.next())
+ {
+ return false;
+ }
+ boolean succeeded = rs.getBoolean(1);
+ return succeeded;
+ }
+ finally
+ {
+ try { stmt.close(); } catch (Throwable e) {}
+ }
+ }
+
+ /**
+ * Builds the schema from scratch or applies the necessary patches to the schema.
+ */
+ private void updateSchema(Configuration cfg, Session session, Connection connection) throws Exception
+ {
+ boolean create = false;
+ try
+ {
+ countAppliedPatches(connection);
+ }
+ catch (Throwable e)
+ {
+ create = true;
+ }
+ if (create)
+ {
+ // the applied patch table is missing - we assume that all other tables are missing
+ // perform a full update using Hibernate-generated statements
+ File tempFile = TempFileProvider.createTempFile("AlfrescoSchemaCreate", ".sql");
+ dumpSchemaCreate(cfg, tempFile);
+ executeScriptFile(cfg, connection, tempFile);
+ // execute post-create scripts (not patches)
+ for (String scriptUrl : this.postCreateScriptUrls)
+ {
+ executeScriptUrl(cfg, connection, scriptUrl);
+ }
+ }
+ else
+ {
+ // we have a database, so just run the update scripts
+ checkSchemaPatchScripts(cfg, session, connection, validateUpdateScriptPatches, false); // check for scripts that must have been run
+ checkSchemaPatchScripts(cfg, session, connection, applyUpdateScriptPatches, true); // execute scripts as required
+ // let Hibernate do any required updates
+ File tempFile = null;
+ Writer writer = null;
+ try
+ {
+ final Dialect dialect = Dialect.getDialect(cfg.getProperties());
+ DatabaseMetadata metadata = new DatabaseMetadata(connection, dialect);
+ String[] sqls = cfg.generateSchemaUpdateScript(dialect, metadata);
+ if (sqls.length > 0)
+ {
+ tempFile = TempFileProvider.createTempFile("AlfrescoSchemaUpdate", ".sql");
+ writer = new BufferedWriter(new FileWriter(tempFile));
+ for (String sql : sqls)
+ {
+ writer.append(sql);
+ writer.append(";\n");
+ }
+ }
+ }
+ finally
+ {
+ if (writer != null)
+ {
+ try {writer.close();} catch (Throwable e) {}
+ }
+ }
+ // execute if there were changes raised by Hibernate
+ if (tempFile != null)
+ {
+ executeScriptFile(cfg, connection, tempFile);
+ }
+ }
+ }
+
+ /**
+ * Check that the necessary scripts have been executed against the database
+ */
+ private void checkSchemaPatchScripts(
+ Configuration cfg,
+ Session session,
+ Connection connection,
+ List scriptPatches,
+ boolean apply) throws Exception
+ {
+ // first check if there have been any applied patches
+ int appliedPatchCount = countAppliedPatches(connection);
+ if (appliedPatchCount == 0)
+ {
+ // This is a new schema, so upgrade scripts are irrelevant
+ // and patches will not have been applied yet
+ return;
+ }
+
+ for (SchemaUpgradeScriptPatch patch : scriptPatches)
+ {
+ final String patchId = patch.getId();
+ final String scriptUrl = patch.getScriptUrl();
+
+ // check if the script was successfully executed
+ boolean wasSuccessfullyApplied = didPatchSucceed(connection, patchId);
+ if (wasSuccessfullyApplied)
+ {
+ // nothing to do - it has been done before
+ continue;
+ }
+ else if (!apply)
+ {
+ // the script was not run and may not be run automatically
+ throw AlfrescoRuntimeException.create(ERR_SCRIPT_NOT_RUN, scriptUrl);
+ }
+ // it wasn't run and it can be run now
+ executeScriptUrl(cfg, connection, scriptUrl);
+ }
+ }
+
+ private void executeScriptUrl(Configuration cfg, Connection connection, String scriptUrl) throws Exception
+ {
+ Dialect dialect = Dialect.getDialect(cfg.getProperties());
+ File scriptFile = getScriptFile(dialect.getClass(), scriptUrl);
+ // check that it exists
+ if (scriptFile == null)
+ {
+ throw AlfrescoRuntimeException.create(ERR_SCRIPT_NOT_FOUND, scriptUrl);
+ }
+ // now execute it
+ executeScriptFile(cfg, connection, scriptFile);
+ }
+
+ /**
+ * Replaces the dialect placeholder in the script URL and attempts to find a file for
+ * it. If not found, the dialect hierarchy will be walked until a compatible script is
+ * found. This makes it possible to have scripts that are generic to all dialects.
+ *
+ * @return Returns the file if found, otherwise null
+ */
+ private File getScriptFile(Class dialectClazz, String scriptUrl) throws Exception
+ {
+ // replace the dialect placeholder
+ String dialectScriptUrl = scriptUrl.replaceAll(PLACEHOLDER_SCRIPT_DIALECT, dialectClazz.getName());
+ // get a handle on the resource
+ try
+ {
+ File scriptFile = ResourceUtils.getFile(dialectScriptUrl);
+ if (scriptFile.exists())
+ {
+ // found a compatible dialect version
+ return scriptFile;
+ }
+ }
+ catch (FileNotFoundException e)
+ {
+ // doesn't exist
+ }
+ // it wasn't found. Get the superclass of the dialect and try again
+ Class superClazz = dialectClazz.getSuperclass();
+ if (Dialect.class.isAssignableFrom(superClazz))
+ {
+ // we still have a Dialect - try again
+ return getScriptFile(superClazz, scriptUrl);
+ }
+ else
+ {
+ // we have exhausted all options
+ return null;
+ }
+ }
+
+ private void executeScriptFile(Configuration cfg, Connection connection, File scriptFile) throws Exception
+ {
+ logger.info(I18NUtil.getMessage(MSG_EXECUTING_SCRIPT, scriptFile));
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(scriptFile), "UTF8"));
+ try
+ {
+ int line = 0;
+ // loop through all statements
+ StringBuilder sb = new StringBuilder(1024);
+ while(true)
+ {
+ String sql = reader.readLine();
+ line++;
+
+ if (sql == null)
+ {
+ // nothing left in the file
+ break;
+ }
+
+ // trim it
+ sql = sql.trim();
+ if (sql.length() == 0 ||
+ sql.startsWith( "--" ) ||
+ sql.startsWith( "//" ) ||
+ sql.startsWith( "/*" ) )
+ {
+ if (sb.length() > 0)
+ {
+ // we have an unterminated statement
+ throw AlfrescoRuntimeException.create(ERR_STATEMENT_TERMINATOR, (line - 1), scriptFile);
+ }
+ // there has not been anything to execute - it's just a comment line
+ continue;
+ }
+ // have we reached the end of a statement?
+ boolean execute = false;
+ if (sql.endsWith(";"))
+ {
+ sql = sql.substring(0, sql.length() - 1);
+ execute = true;
+ }
+ // append to the statement being built up
+ sb.append(" ").append(sql);
+ // execute, if required
+ if (execute)
+ {
+ Statement stmt = connection.createStatement();
+ try
+ {
+ sql = sb.toString();
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Executing statment: " + sql);
+ }
+ stmt.execute(sql);
+ sb = new StringBuilder(1024);
+ }
+ finally
+ {
+ try { stmt.close(); } catch (Throwable e) {}
+ }
+ }
+ }
+ }
+ finally
+ {
+ try { reader.close(); } catch (Throwable e) {}
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
index 18b1922b4e..4fe89d582b 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
@@ -586,13 +586,20 @@ public class FileFolderServiceImpl implements FileFolderService
}
else
{
- // copy the node
- targetNodeRef = copyService.copy(
- sourceNodeRef,
- targetParentRef,
- assocRef.getTypeQName(),
- qname,
- true);
+ try
+ {
+ // copy the node
+ targetNodeRef = copyService.copy(
+ sourceNodeRef,
+ targetParentRef,
+ assocRef.getTypeQName(),
+ qname,
+ true);
+ }
+ catch (DuplicateChildNodeNameException e)
+ {
+ throw new FileExistsException(targetParentRef, newName);
+ }
}
// Only update the name if it has changed
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
index d42eccd354..279cdb5922 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
@@ -336,6 +336,10 @@ public class FileFolderServiceImplTest extends TestCase
// make sure that it is an immediate child of the root
List checkFileInfos = fileFolderService.search(workingRootNodeRef, NAME_L1_FOLDER_A, false);
assertEquals("Folder not copied to root", 1, checkFileInfos.size());
+ // copy properly
+ FileInfo checkFileInfo = fileFolderService.copy(folderToCopyRef, null, "new name");
+ checkFileInfos = fileFolderService.search(workingRootNodeRef, checkFileInfo.getName(), false);
+ assertEquals("Folder not renamed in root", 1, checkFileInfos.size());
// attempt illegal copy (existing)
try
{
@@ -346,10 +350,6 @@ public class FileFolderServiceImplTest extends TestCase
{
// expected
}
- // copy properly
- FileInfo checkFileInfo = fileFolderService.copy(folderToCopyRef, null, "new name");
- checkFileInfos = fileFolderService.search(workingRootNodeRef, checkFileInfo.getName(), false);
- assertEquals("Folder not renamed in root", 1, checkFileInfos.size());
}
public void testCreateFolder() throws Exception
diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
index 0a5709f410..f6de57d293 100644
--- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
+++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java
@@ -1630,10 +1630,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
NodeRef defRef = pathDefRef.getChildRef();
// now browse down using the node service
- NodeRef checkParentRef = nodeService.getChildByName(rootNodeRef, ASSOC_TYPE_QNAME_TEST_CHILDREN, parentRef.getId());
- assertNotNull("First level, non-named node not found", checkParentRef);
- assertEquals(parentRef, checkParentRef);
- NodeRef checkAbcRef = nodeService.getChildByName(checkParentRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "abc");
+ NodeRef checkAbcRef = nodeService.getChildByName(parentRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "abc");
assertNotNull("Second level, named node 'ABC' not found", checkAbcRef);
assertEquals(abcRef, checkAbcRef);
NodeRef checkDefRef = nodeService.getChildByName(checkAbcRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "def");
diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
index 9972e889a2..85a214f845 100644
--- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
@@ -149,7 +149,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
else
{
return new NodeRef.Status(
- nodeStatus.getChangeTxnId(),
+ nodeStatus.getTransaction().getChangeTxnId(),
nodeStatus.isDeleted());
}
}
@@ -1440,11 +1440,11 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
// update old status
NodeStatus oldNodeStatus = nodeDaoService.getNodeStatus(oldNodeRef, true);
oldNodeStatus.setNode(null);
- oldNodeStatus.setChangeTxnId(txnId);
+ oldNodeStatus.getTransaction().setChangeTxnId(txnId);
// create the new status
NodeStatus newNodeStatus = nodeDaoService.getNodeStatus(newNodeRef, true);
newNodeStatus.setNode(nodeToMove);
- newNodeStatus.setChangeTxnId(txnId);
+ newNodeStatus.getTransaction().setChangeTxnId(txnId);
}
}
diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
index 6ecccd65d1..b2872cdeaf 100644
--- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
@@ -16,9 +16,14 @@
*/
package org.alfresco.repo.node.db.hibernate;
+import java.io.Serializable;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.zip.CRC32;
import org.alfresco.error.AlfrescoRuntimeException;
@@ -28,13 +33,17 @@ import org.alfresco.repo.domain.Node;
import org.alfresco.repo.domain.NodeAssoc;
import org.alfresco.repo.domain.NodeKey;
import org.alfresco.repo.domain.NodeStatus;
+import org.alfresco.repo.domain.Server;
import org.alfresco.repo.domain.Store;
import org.alfresco.repo.domain.StoreKey;
+import org.alfresco.repo.domain.Transaction;
import org.alfresco.repo.domain.hibernate.ChildAssocImpl;
import org.alfresco.repo.domain.hibernate.NodeAssocImpl;
import org.alfresco.repo.domain.hibernate.NodeImpl;
import org.alfresco.repo.domain.hibernate.NodeStatusImpl;
+import org.alfresco.repo.domain.hibernate.ServerImpl;
import org.alfresco.repo.domain.hibernate.StoreImpl;
+import org.alfresco.repo.domain.hibernate.TransactionImpl;
import org.alfresco.repo.node.db.NodeDaoService;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionalDao;
@@ -71,9 +80,14 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
private static final String QUERY_GET_TARGET_ASSOCS = "node.GetTargetAssocs";
private static final String QUERY_GET_SOURCE_ASSOCS = "node.GetSourceAssocs";
private static final String QUERY_GET_CONTENT_DATA_STRINGS = "node.GetContentDataStrings";
+ private static final String QUERY_GET_SERVER_BY_IPADDRESS = "server.getServerByIpAddress";
/** a uuid identifying this unique instance */
- private String uuid;
+ private final String uuid;
+
+ private final ReadLock serverReadLock;
+ private final WriteLock serverWriteLock;
+ private Server server;
/**
*
@@ -81,6 +95,10 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
public HibernateNodeDaoServiceImpl()
{
this.uuid = GUID.generate();
+
+ ReentrantReadWriteLock serverReadWriteLock = new ReentrantReadWriteLock();
+ serverReadLock = serverReadWriteLock.readLock();
+ serverWriteLock = serverReadWriteLock.writeLock();
}
/**
@@ -108,6 +126,93 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
return uuid.hashCode();
}
+ /**
+ * Gets/creates the server instance to use for the life of this instance
+ */
+ private Server getServer()
+ {
+ // get readlock
+ serverReadLock.lock();
+ try
+ {
+ if (server != null)
+ {
+ return server;
+ }
+ }
+ finally
+ {
+ serverReadLock.unlock();
+ }
+ // get the write lock
+ serverWriteLock.lock();
+ try
+ {
+ final String ipAddress = InetAddress.getLocalHost().getHostAddress();
+ HibernateCallback callback = new HibernateCallback()
+ {
+ public Object doInHibernate(Session session)
+ {
+ Query query = session
+ .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_SERVER_BY_IPADDRESS)
+ .setString("ipAddress", ipAddress);
+ return query.uniqueResult();
+ }
+ };
+ server = (Server) getHibernateTemplate().execute(callback);
+ // create it if it doesn't exist
+ if (server == null)
+ {
+ server = new ServerImpl();
+ server.setIpAddress(ipAddress);
+ try
+ {
+ getSession().save(server);
+ }
+ catch (DataIntegrityViolationException e)
+ {
+ // get it again
+ server = (Server) getHibernateTemplate().execute(callback);
+ if (server == null)
+ {
+ throw new AlfrescoRuntimeException("Unable to create server instance: " + ipAddress);
+ }
+ }
+ }
+ return server;
+ }
+ catch (Exception e)
+ {
+ throw new AlfrescoRuntimeException("Failed to create server instance", e);
+ }
+ finally
+ {
+ serverWriteLock.unlock();
+ }
+ }
+
+ private static final String RESOURCE_KEY_TRANSACTION_ID = "hibernate.transaction.id";
+ private Transaction getCurrentTransaction()
+ {
+ Transaction transaction = null;
+ Serializable txnId = (Serializable) AlfrescoTransactionSupport.getResource(RESOURCE_KEY_TRANSACTION_ID);
+ if (txnId == null)
+ {
+ // no transaction instance has been bound to the transaction
+ transaction = new TransactionImpl();
+ transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
+ transaction.setServer(getServer());
+ txnId = getHibernateTemplate().save(transaction);
+ // bind the id
+ AlfrescoTransactionSupport.bindResource(RESOURCE_KEY_TRANSACTION_ID, txnId);
+ }
+ else
+ {
+ transaction = (Transaction) getHibernateTemplate().get(TransactionImpl.class, txnId);
+ }
+ return transaction;
+ }
+
/**
* Does this Session contain any changes which must be
* synchronized with the store?
@@ -218,7 +323,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
{
status = new NodeStatusImpl();
status.setKey(nodeKey);
- status.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
+ status.setTransaction(getCurrentTransaction());
getHibernateTemplate().save(status);
}
// done
@@ -237,7 +342,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
}
else
{
- status.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
+ status.getTransaction().setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
}
}
@@ -259,13 +364,13 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
// If that is the case, then the session has to be flushed so that the database
// constraints aren't violated as the node creation will write to the database to
// get an ID
- if (status.getChangeTxnId().equals(AlfrescoTransactionSupport.getTransactionId()))
+ if (status.getTransaction().getChangeTxnId().equals(AlfrescoTransactionSupport.getTransactionId()))
{
// flush
getHibernateTemplate().flush();
}
}
-
+
// build a concrete node based on a bootstrap type
Node node = new NodeImpl();
// set other required properties
@@ -277,7 +382,11 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
// set required status properties
status.setNode(node);
- status.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
+ // assign a transaction
+ if (status.getTransaction() == null)
+ {
+ status.setTransaction(getCurrentTransaction());
+ }
// persist the nodestatus
getHibernateTemplate().save(status);
@@ -331,7 +440,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
NodeRef nodeRef = node.getNodeRef();
NodeStatus nodeStatus = getNodeStatus(nodeRef, true);
nodeStatus.setNode(null);
- nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
+ nodeStatus.getTransaction().setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
// finally delete the node
getHibernateTemplate().delete(node);
// flush to ensure constraints can't be violated
@@ -371,7 +480,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
{
/*
* This initial child association creation will fail IFF there is already
- * an association of the given type between the two nodes. For new association
+ * an association of the given type and name between the two nodes. For new association
* creation, this can only occur if two transactions attempt to create a secondary
* child association between the same two nodes. As this is unlikely, it is
* appropriate to just throw a runtime exception and let the second transaction
@@ -383,28 +492,18 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
* if the association is recreated subsequently.
*/
- String uuid = childNode.getUuid();
+ // assign a random name to the node
+ String randomName = GUID.generate();
ChildAssoc assoc = new ChildAssocImpl();
assoc.setTypeQName(assocTypeQName);
- assoc.setChildNodeName(getShortName(uuid));
- assoc.setChildNodeNameCrc(getCrc(uuid));
+ assoc.setChildNodeName(randomName);
+ assoc.setChildNodeNameCrc(-1L); // random names compete only with each other
assoc.setQname(qname);
assoc.setIsPrimary(isPrimary);
assoc.buildAssociation(parentNode, childNode);
// persist it, catching the duplicate child name
- try
- {
- getHibernateTemplate().save(assoc);
- }
- catch (DataIntegrityViolationException e)
- {
- throw new AlfrescoRuntimeException("An association already exists between the two nodes: \n" +
- " parent: " + parentNode.getId() + "\n" +
- " child: " + childNode.getId() + "\n" +
- " assoc: " + assocTypeQName,
- e);
- }
+ getHibernateTemplate().save(assoc);
// done
return assoc;
}
@@ -422,17 +521,22 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
*/
String childNameNew = null;
+ long crc = -1;
if (childName == null)
{
- childNameNew = childAssoc.getChild().getUuid();
+ // random names compete only with each other, i.e. not at all
+ childNameNew = GUID.generate();
+ crc = -1;
}
else
{
+ // assigned names compete exactly
childNameNew = childName.toLowerCase();
+ crc = getCrc(childNameNew);
}
final String childNameNewShort = getShortName(childNameNew);
- final long childNameNewCrc = getCrc(childNameNew);
+ final long childNameNewCrc = crc;
// check if the name has changed
if (childAssoc.getChildNodeNameCrc() == childNameNewCrc)
diff --git a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java b/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java
index 3000634953..54fb4c460b 100644
--- a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java
+++ b/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java
@@ -62,13 +62,13 @@ import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
* database is static then the L2 cache usage can be set to use
* the NORMAL mode. REFRESH should be
* used where the server will still be accessed from some clients
- * despite the database changing.
+ * despite the database changing. NORMAL can be used
+ * in the case of the caches being clustered, i.e. the caches will
+ * not be out of date w.r.t. the database.
*
*
- * This process should not run continuously on a live
- * server as it would be performing unecessary work.
- * If it was left running, however, it would not
- * lead to data corruption or such-like. Use the
+ * This process should only be used continuously where the index
+ * transactions are following the database transactions. Use the
* {@link #setRunContinuously(boolean) runContinuously} property
* to change this behaviour.
*
@@ -91,7 +91,7 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I
private static boolean started = false;
/** The current transaction ID being processed */
private static String currentTxnId = START_TXN_ID;
- /** kept to notify the thread that it should quite */
+ /** kept to notify the thread that it should quit */
private boolean killThread = false;
/** provides transactions to atomically index each missed transaction */
@@ -104,8 +104,6 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I
private SearchService searcher;
/** the component giving direct access to node instances */
private NodeService nodeService;
- /** the stores to reindex */
- private List storeRefs;
/** set this to run the index recovery component */
private boolean executeFullRecovery;
/** set this on to keep checking for new transactions and never stop */
@@ -125,8 +123,6 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I
public FullIndexRecoveryComponent()
{
- this.storeRefs = new ArrayList(2);
-
this.killThread = false;
this.executeFullRecovery = false;
this.runContinuously = false;
@@ -193,21 +189,6 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I
this.nodeService = nodeService;
}
- /**
- * Set the stores that need reindexing
- *
- * @param storeRefStrings a list of strings representing store references
- */
- public void setStores(List storeRefStrings)
- {
- storeRefs.clear();
- for (String storeRefStr : storeRefStrings)
- {
- StoreRef storeRef = new StoreRef(storeRefStr);
- storeRefs.add(storeRef);
- }
- }
-
/**
* Set this to true to initiate the full index recovery.
*
@@ -299,6 +280,7 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I
{
public Object doWork()
{
+ List storeRefs = nodeService.getStores();
// reindex each store
for (StoreRef storeRef : storeRefs)
{
@@ -352,8 +334,7 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I
if (logger.isDebugEnabled())
{
logger.debug("Full index recovery thread started: \n" +
- " continuous: " + runContinuously + "\n" +
- " stores: " + storeRefs);
+ " continuous: " + runContinuously);
}
}
}
@@ -377,8 +358,8 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I
// reindex nodes
List txnsIndexed = FullIndexRecoveryComponent.this.reindexNodes();
// reindex missing content
- @SuppressWarnings("unused")
- int missingContentCount = FullIndexRecoveryComponent.this.reindexMissingContent();
+// @SuppressWarnings("unused")
+// int missingContentCount = FullIndexRecoveryComponent.this.reindexMissingContent();
// check if the process should terminate
if (txnsIndexed.size() == 0 && !runContinuously)
{
@@ -417,73 +398,6 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I
}
}
- /**
- * @return Returns the number of documents reindexed
- */
- private int reindexMissingContent()
- {
- int count = 0;
- for (StoreRef storeRef : storeRefs)
- {
- count += reindexMissingContent(storeRef);
- }
- return count;
- }
-
- /**
- * @param storeRef the store to check for missing content
- * @return Returns the number of documents reindexed
- */
- private int reindexMissingContent(StoreRef storeRef)
- {
- SearchParameters sp = new SearchParameters();
- sp.addStore(storeRef);
-
- // search for it in the index
- String query = "TEXT:" + LuceneIndexerImpl.NOT_INDEXED_CONTENT_MISSING;
- sp.setLanguage(SearchService.LANGUAGE_LUCENE);
- sp.setQuery(query);
- ResultSet results = null;
- try
- {
- results = searcher.query(sp);
-
- int count = 0;
- // loop over the results and get the details of the nodes that have missing content
- List assocRefs = results.getChildAssocRefs();
- for (ChildAssociationRef assocRef : assocRefs)
- {
- final NodeRef childNodeRef = assocRef.getChildRef();
- // prompt for a reindex - it might fail again, but we just keep plugging away
- TransactionWork