From 80cce9c0f23e64e93bbecd6e33064e7d6305b656 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Tue, 1 Nov 2011 16:07:11 +0000 Subject: [PATCH] Merged DEV to HEAD 31489: Fix MT NodeRef translation in policy filter (ALF-10178) 31452: Sync DEV branch with HEAD 30312: Reintegrated HEAD 30281: Comprehensive DEBUG logging to track behaviour enable/disable states for transactions - Part of ALF-10178: BehaviourFilter fails when nesting disable/enable calls 30280: Fixed importer's use of behaviour filter - The change in the BehaviourFilter contract means that all disable calls must be matched with an equivalent enable call. Enabling globally no longer wipes out vetos put in place by other code. - Part of ALF-10178: BehaviourFilter fails when nesting disable/enable calls 30279: Removed unnecessary behaviour enablement checks is VersionService (ALF-10178) 30278: Fixed behaviour re-enabling for StoreSelectorAspectContentStore (ALF-10178) 30240: Fixed ALF-10178: BehaviourFilter fails when nesting disable/enable calls - Behaviour enable/disable now uses reference counting to check the state of different behaviour levels - Added unit test to test - Re-enabled test for ALF-10177: Test disabled: CheckOutCheckInServiceImplTest.testalfrescoCheckoutDoesntModifyNode but renamed to CheckOutCheckInServiceImplTest.testAlfrescoCheckoutDoesNotModifyNode - Going into DEV branch to run through tests 30236: Branch for fixing BehaviourFilter nesting git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@31619 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../filesys/repo/ContentDiskDriver.java | 10 +- .../patch/impl/CopiedFromAspectPatch.java | 4 +- .../DisableAuditableBehaviourInterceptor.java | 23 +- .../coci/CheckOutCheckInServiceImplTest.java | 6 +- .../repo/importer/ImporterComponent.java | 50 +- .../repo/node/db/DbNodeServiceImpl.java | 14 +- .../alfresco/repo/policy/BehaviourFilter.java | 126 +++-- .../repo/policy/BehaviourFilterImpl.java | 494 ++++++++++++++---- .../PolicyComponentTransactionTest.java | 94 ++++ .../publishing/PublishingEventProcessor.java | 4 +- .../UpdateTagScopesActionExecuter.java | 4 +- .../AlfrescoTransactionSupportTest.java | 16 + .../TransactionalResourceHelper.java | 127 +++-- .../transfer/RepoTransferReceiverImpl.java | 5 +- .../repo/version/Version2ServiceImpl.java | 11 +- 15 files changed, 709 insertions(+), 279 deletions(-) diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java index 97f504c588..35e3980837 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java @@ -37,11 +37,8 @@ import javax.transaction.UserTransaction; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.filesys.alfresco.AlfrescoContext; -import org.alfresco.filesys.alfresco.AlfrescoDiskDriver; import org.alfresco.filesys.alfresco.AlfrescoNetworkFile; -import org.alfresco.filesys.config.ServerConfigurationBean; import org.alfresco.filesys.alfresco.AlfrescoTxDiskDriver; -import org.alfresco.filesys.alfresco.ShuffleCache; import org.alfresco.jlan.server.SrvSession; import org.alfresco.jlan.server.core.DeviceContext; import org.alfresco.jlan.server.core.DeviceContextException; @@ -4228,7 +4225,7 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter // copy over the node creator and owner properties // need to disable the auditable aspect first to prevent default audit behaviour - boolean alreadyDisabled = policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); + policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); try { nodeService.setProperty(toNode, ContentModel.PROP_CREATOR, nodeService.getProperty(fromNode, ContentModel.PROP_CREATOR)); @@ -4236,10 +4233,7 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter } finally { - if(!alreadyDisabled) - { - policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); - } + policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); } Set permissions = permissionService.getAllSetPermissions(fromNode); diff --git a/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java index 947bfa50a6..21a51d1e1b 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/CopiedFromAspectPatch.java @@ -262,7 +262,7 @@ public class CopiedFromAspectPatch extends AbstractPatch public void process(Pair entry) throws Throwable { // Disable auditable aspect - behaviourFilter.disableAllBehaviours(); + behaviourFilter.disableBehaviour(); // Disable rules ruleService.disableRules(); try @@ -272,7 +272,7 @@ public class CopiedFromAspectPatch extends AbstractPatch finally { ruleService.enableRules(); - behaviourFilter.enableAllBehaviours(); + behaviourFilter.enableBehaviour(); } } @Override diff --git a/source/java/org/alfresco/repo/audit/DisableAuditableBehaviourInterceptor.java b/source/java/org/alfresco/repo/audit/DisableAuditableBehaviourInterceptor.java index 84029b98d7..638146487e 100644 --- a/source/java/org/alfresco/repo/audit/DisableAuditableBehaviourInterceptor.java +++ b/source/java/org/alfresco/repo/audit/DisableAuditableBehaviourInterceptor.java @@ -20,7 +20,9 @@ package org.alfresco.repo.audit; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.repo.policy.BehaviourFilter; @@ -32,7 +34,7 @@ import org.aopalliance.intercept.MethodInvocation; /** * An interceptor that disables and then enables ASPECT_AUDITABLE behaviours * around method calls. - * + *
    *
  • The name of the method must match a supplied list (See * {@link #setMethodNames(List)}).
  • *
  • For this interceptor to disable and enable policy behaviour, the first @@ -42,16 +44,18 @@ import org.aopalliance.intercept.MethodInvocation; * values (See {@link #setArgumentValues(List)}. The second argument must be * a QName. If a list is not supplied the second argument is not checked.
  • *
  • The BehaviourFilter to be enabled or disabled must be set (See - * {@link #setBehaviourFilter(BehaviourFilter)}). + * {@link #setBehaviourFilter(BehaviourFilter)}).
  • + *
* * @author Stas Sokolovsky */ public class DisableAuditableBehaviourInterceptor implements MethodInterceptor { private BehaviourFilter behaviourFilter; - private List methodNames; - private List argumentValues; + private Set methodNames = new HashSet(0); + private Set argumentQNameValues = new HashSet(0); + @SuppressWarnings("unchecked") public Object invoke(MethodInvocation methodInvocation) throws Throwable { String methodName = methodInvocation.getMethod().getName(); @@ -77,7 +81,7 @@ public class DisableAuditableBehaviourInterceptor implements MethodInterceptor if (behaviourFilter != null && methodNames.contains(methodName) && - (arg1 == null || argumentValues.contains(arg1.toString()))) + (arg1 == null || argumentQNameValues.contains(arg1))) { for (NodeRef nodeRef : nodes) { @@ -108,11 +112,16 @@ public class DisableAuditableBehaviourInterceptor implements MethodInterceptor public void setMethodNames(List methodNames) { - this.methodNames = methodNames; + this.methodNames = new HashSet(methodNames); } public void setArgumentValues(List argumentValues) { - this.argumentValues = argumentValues; + this.argumentQNameValues = new HashSet(argumentValues.size()*2+1); + for (String argumentValue : argumentValues) + { + QName argumentQNameValue = QName.createQName(argumentValue); + argumentQNameValues.add(argumentQNameValue); + } } } diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java index 75874857db..b1301fd7de 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java @@ -637,7 +637,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest }); } - public void testalfrescoCheckoutDoesntModifyNode() + public void testAlfrescoCheckoutDoesNotModifyNode() { String adminUser = AuthenticationUtil.getAdminUserName(); AuthenticationUtil.setFullyAuthenticatedUser(adminUser); @@ -659,9 +659,9 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest cociService.cancelCheckout(copy); modifier = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); -// assertEquals("Cancel checkout should not cause the modifier to change!", initModifier, modifier); + assertEquals("Cancel checkout should not cause the modifier to change!", initModifier, modifier); modified = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); -// assertEquals("Cancel checkout should not cause the modified date to change!", initModified, modified); + assertEquals("Cancel checkout should not cause the modified date to change!", initModified, modified); copy = cociService.checkout( nodeRef, diff --git a/source/java/org/alfresco/repo/importer/ImporterComponent.java b/source/java/org/alfresco/repo/importer/ImporterComponent.java index 40c772a29f..9422da1089 100644 --- a/source/java/org/alfresco/repo/importer/ImporterComponent.java +++ b/source/java/org/alfresco/repo/importer/ImporterComponent.java @@ -35,8 +35,6 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.importer.view.NodeContext; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.security.authentication.AuthenticationContext; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.alfresco.repo.transaction.TransactionListener; import org.alfresco.repo.version.Version2Model; import org.alfresco.repo.version.common.VersionUtil; import org.alfresco.service.cmr.dictionary.AssociationDefinition; @@ -65,16 +63,15 @@ import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.cmr.view.ImportPackageHandler; import org.alfresco.service.cmr.view.ImporterBinding; +import org.alfresco.service.cmr.view.ImporterBinding.UUID_BINDING; import org.alfresco.service.cmr.view.ImporterException; import org.alfresco.service.cmr.view.ImporterProgress; import org.alfresco.service.cmr.view.ImporterService; import org.alfresco.service.cmr.view.Location; -import org.alfresco.service.cmr.view.ImporterBinding.UUID_BINDING; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.hibernate.engine.TransactionHelper; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -774,7 +771,7 @@ public class ImporterComponent */ public void childrenImported(NodeRef nodeRef) { - behaviourFilter.enableBehaviours(nodeRef); + behaviourFilter.enableBehaviour(nodeRef); ruleService.enableRules(nodeRef); } @@ -845,12 +842,12 @@ public class ImporterComponent } // Set node reference on source node - Set disabledBehaviours = getDisabledBehaviours(importedRef.context); + Set nodeTypeAndAspects = getNodeTypeAndAspects(importedRef.context); try { - for (QName disabledBehaviour: disabledBehaviours) + for (QName typeOrAspect: nodeTypeAndAspects) { - behaviourFilter.disableBehaviour(importedRef.context.getNodeRef(), disabledBehaviour); + behaviourFilter.disableBehaviour(importedRef.context.getNodeRef(), typeOrAspect); } nodeService.setProperty(importedRef.context.getNodeRef(), importedRef.property, refProperty); if (progress != null) @@ -860,7 +857,10 @@ public class ImporterComponent } finally { - behaviourFilter.enableBehaviours(importedRef.context.getNodeRef()); + for (QName typeOrAspect: nodeTypeAndAspects) + { + behaviourFilter.enableBehaviour(importedRef.context.getNodeRef(), typeOrAspect); + } } } @@ -873,7 +873,7 @@ public class ImporterComponent */ public void error(Throwable e) { - behaviourFilter.enableAllBehaviours(); + behaviourFilter.enableBehaviour(); reportError(e); } @@ -1001,12 +1001,12 @@ public class ImporterComponent } /** - * For the given import node, return the behaviours to disable during import + * For the given import node, return the type and aspects to import * * @param context import node - * @return the disabled behaviours + * @return the type and aspects in the import */ - private Set getDisabledBehaviours(ImportNode context) + private Set getNodeTypeAndAspects(ImportNode context) { Set classNames = new HashSet(); @@ -1352,18 +1352,12 @@ public class ImporterComponent throw new ImporterException("Cannot determine child name of node (type: " + nodeType.getName() + ")"); } - // Create initial node (but, first disable behaviour for the node to be created) - Set disabledBehaviours = getDisabledBehaviours(node); - List alreadyDisabledBehaviours = new ArrayList(); - for (QName disabledBehaviour: disabledBehaviours) + // Disable the import-wide behaviours (because the node doesn't exist, yet) + Set nodeTypeAndAspects = getNodeTypeAndAspects(node); + for (QName typeOrAspect: nodeTypeAndAspects) { - boolean alreadyDisabled = behaviourFilter.disableBehaviour(disabledBehaviour); - if (alreadyDisabled) - { - alreadyDisabledBehaviours.add(disabledBehaviour); - } + behaviourFilter.disableBehaviour(typeOrAspect); } - disabledBehaviours.removeAll(alreadyDisabledBehaviours); // Build initial map of properties Map initialProperties = bindProperties(node); @@ -1403,15 +1397,13 @@ public class ImporterComponent } } + // Re-enable the import-wide behaviours // Disable behaviour for the node until the complete node (and its children have been imported) - for (QName disabledBehaviour : disabledBehaviours) + for (QName typeOrAspect: nodeTypeAndAspects) { - behaviourFilter.enableBehaviour(disabledBehaviour); - } - for (QName disabledBehaviour : disabledBehaviours) - { - behaviourFilter.disableBehaviour(nodeRef, disabledBehaviour); + behaviourFilter.enableBehaviour(typeOrAspect); } + behaviourFilter.disableBehaviour(nodeRef); // TODO: Replace this with appropriate rule/action import handling ruleService.disableRules(nodeRef); diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 50efeff45c..bf4e9cb2f7 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -2181,17 +2181,14 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl */ private void archiveNode(NodeRef nodeRef, StoreRef archiveStoreRef) { - boolean wasDisabled = policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); + policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); try { archiveNodeImpl(nodeRef, archiveStoreRef); } finally { - if (!wasDisabled) - { - policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); - } + policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); } } @@ -2256,17 +2253,14 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl */ public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef destinationParentNodeRef, QName assocTypeQName, QName assocQName) { - boolean wasDisabled = policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); + policyBehaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); try { return restoreNodeImpl(archivedNodeRef, destinationParentNodeRef, assocTypeQName, assocQName); } finally { - if (!wasDisabled) - { - policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); - } + policyBehaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); } } diff --git a/source/java/org/alfresco/repo/policy/BehaviourFilter.java b/source/java/org/alfresco/repo/policy/BehaviourFilter.java index bac0cc49ef..1b37d3b2d1 100644 --- a/source/java/org/alfresco/repo/policy/BehaviourFilter.java +++ b/source/java/org/alfresco/repo/policy/BehaviourFilter.java @@ -26,37 +26,71 @@ import org.alfresco.service.namespace.QName; * * @See org.alfresco.repo.policy.PolicyComponent * - * @author David Caruana + * @author Derek Hulley */ public interface BehaviourFilter { + /** + * @deprecated Since 4.0 use {@link #enableBehaviour(NodeRef)} + */ + public void enableBehaviours(NodeRef nodeRef); + + /** + * @deprecated Since 4.0 use {@link #enableBehaviour(NodeRef)} + */ + public void disableAllBehaviours(); + + /** + * @deprecated Since 4.0 use {@link #disableBehaviour()} + */ + public void enableAllBehaviours(); + + /** + * Disable behaviour for all types + *

+ * The change applies ONLY to the current transaction. + */ + public void disableBehaviour(); + /** * Disable behaviour for a type or aspect for all nodes. *

* The change applies ONLY to the current transaction. * - * @param className the type/aspect behaviour to disable - * @return true => already disabled + * @param className the type/aspect behaviour to disable */ - public boolean disableBehaviour(QName className); + public void disableBehaviour(QName className); /** - * Disable behaviour for specific node + * Disable behaviour for specific node and class *

* The change applies ONLY to the current transaction. * - * @param nodeRef the node to disable for - * @param className the type/aspect behaviour to disable - * @return true => already disabled + * @param nodeRef the node to disable for + * @param className the type/aspect behaviour to disable */ - public boolean disableBehaviour(NodeRef nodeRef, QName className); + public void disableBehaviour(NodeRef nodeRef, QName className); + + /** + * Disable all behaviours for a given node + * + * @param nodeRef the node to disable for + */ + public void disableBehaviour(NodeRef nodeRef); + /** + * Enable behaviours for all classes. + *

+ * The change applies ONLY to the current transaction. + */ + public void enableBehaviour(); + /** * Enable behaviour for all nodes *

* The change applies ONLY to the current transaction. * - * @param className the type/aspect behaviour to enable + * @param className the type/aspect behaviour to enable */ public void enableBehaviour(QName className); @@ -65,53 +99,45 @@ public interface BehaviourFilter *

* The change applies ONLY to the current transaction. * - * @param nodeRef the node to enable for - * @param className the type/aspect behaviour to enable + * @param nodeRef the node to enable for + * @param className the type/aspect behaviour to enable or null for all classes */ public void enableBehaviour(NodeRef nodeRef, QName className); + + /** + * Enable behaviour for a specific node + *

+ * The change applies ONLY to the current transaction. + * + * @param nodeRef the node to enable for + * + * @since 4.0 + */ + public void enableBehaviour(NodeRef nodeRef); /** - * Enable all behaviours for specific node + * Determine if behaviour is globally enabled. *

* The change applies ONLY to the current transaction. * - * @param nodeRef the node to enable for + * @return true => behaviour is enabled + * + * @since 4.0 */ - public void enableBehaviours(NodeRef nodeRef); + public boolean isEnabled(); /** - * Disable all behaviours. Once this method is called the node and class level filters, enableBehaviours and disableBehaviours - * methods have no effect, every behaviour is disabled. - * EnableAllBehaviours reverses the result of calling this method. - *

- * Calling this method may result in nodes existing in your repository that do not conform to your policies. - * - *

- * The change applies ONLY to the current transaction. - * @see #enableAllBehaviours - */ - public void disableAllBehaviours(); - - /** - * Enable all behaviours i.e. undo all disable calls - at the global, - * node and class level. - *

- * The change applies ONLY to the current transaction. - */ - public void enableAllBehaviours(); - - /** - * Determine if behaviour is enabled across all nodes. + * Determine if behaviour is enabled for a class. *

* The change applies ONLY to the current transaction. * - * @param className the behaviour to test for - * @return true => behaviour is enabled + * @param className the behaviour to test for + * @return true => behaviour is enabled */ public boolean isEnabled(QName className); /** - * Determine if behaviour is enabled for specific node. + * Determine if behaviour is enabled for specific node and class. *

* Note: A node behaviour is enabled only when: * a) the behaviour is not disabled across all nodes @@ -119,18 +145,28 @@ public interface BehaviourFilter *

* The change applies ONLY to the current transaction. * - * @param nodeRef the node to test for - * @param className the behaviour to test for - * @return true => behaviour is enabled + * @param nodeRef the node to test for + * @param className the behaviour to test for + * @return true => behaviour is enabled */ public boolean isEnabled(NodeRef nodeRef, QName className); /** - * Determine if any behaviours have been disabled? + * Determine if behaviour is enabled for a specific node. *

* The change applies ONLY to the current transaction. * - * @return true => behaviours have been filtered + * @param nodeRef the node to test for + * @return true => behaviour is enabled + */ + public boolean isEnabled(NodeRef nodeRef); + + /** + * Determine if any behaviours have been disabled or altered. + *

+ * The change applies ONLY to the current transaction. + * + * @return true => behaviours have been altered */ public boolean isActivated(); } diff --git a/source/java/org/alfresco/repo/policy/BehaviourFilterImpl.java b/source/java/org/alfresco/repo/policy/BehaviourFilterImpl.java index aad6c64612..4c2676647b 100644 --- a/source/java/org/alfresco/repo/policy/BehaviourFilterImpl.java +++ b/source/java/org/alfresco/repo/policy/BehaviourFilterImpl.java @@ -18,33 +18,66 @@ */ package org.alfresco.repo.policy; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; import java.util.Map; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import org.apache.commons.lang.mutable.MutableInt; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.ParameterCheck; /** * Implementation of Behaviour Filter. All methods operate on transactionally-bound * resources. Behaviour will therefore never span transactions; the filter state has * the same lifespan as the transaction in which it was created. + *

+ * Since 4.0, the behaviour enabling/disabling is recorded using reference counting, + * meaning that the outermost disable call in a stack has an effective veto. Use + * proper try-finally patterns to ensure behaviour is released after it is no longer + * needed. + *


+ * behaviourFilter.disableBehaviour(abc);
+ * try
+ * {
+ *    behaviourFilter.disableBehaviour(abc);
+ *    try
+ *    {
+ *       // Do something that might have triggered 'abc' but will not
+ *    }
+ *    finally
+ *    {
+ *        behaviourFilter.enableBehaviour(abc);
+ *    }
+ *    // Do something that might have triggered 'abc' but will not despite the last enable call
+ * }
+ * finally
+ * {
+ *     behaviourFilter.enableBehaviour(abc);
+ * }
+ * 
+ *

+ * Multitenancy and disabling by NodeRef:
+ * Conversions based on the current tenant context are done automatically. * - * @author David Caruana + * @author Derek Hulley */ public class BehaviourFilterImpl implements BehaviourFilter { - private static final String KEY_GLOBAL_FILTER = "BehaviourFilterImpl.gloalFilter"; - private static final String KEY_CLASS_FILTER = "BehaviourFilterImpl.classFilter"; - private static final String KEY_NODEREF_FILTER = "BehaviourFilterImpl.nodeRefFilter"; + private static final String KEY_FILTER_COUNT = "BehaviourFilterImpl.filterCount"; + private static final String KEY_GLOBAL_FILTERS = "BehaviourFilterImpl.globalFilters"; + private static final String KEY_CLASS_FILTERS = "BehaviourFilterImpl.classFilters"; + private static final String KEY_INSTANCE_CLASS_FILTERS = "BehaviourFilterImpl.instanceClassFilters"; + private static final String KEY_INSTANCE_FILTERS = "BehaviourFilterImpl.instanceFilters"; + + private static final Log logger = LogFactory.getLog(BehaviourFilterImpl.class); - // Dictionary Service private DictionaryService dictionaryService; - - // Tenant Service private TenantService tenantService; /** @@ -62,150 +95,389 @@ public class BehaviourFilterImpl implements BehaviourFilter { this.tenantService = tenantService; } - - public boolean disableBehaviour(QName className) - { - List classFilters = TransactionalResourceHelper.getList(KEY_CLASS_FILTER); - boolean alreadyDisabled = classFilters.contains(className); - if (!alreadyDisabled) - { - classFilters.add(className); - } - return alreadyDisabled; - } - - public boolean disableBehaviour(NodeRef nodeRef, QName className) - { - nodeRef = tenantService.getName(nodeRef); - - Map> nodeRefFilters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER); - List classNames = nodeRefFilters.get(nodeRef); - if (classNames == null) - { - classNames = new ArrayList(); - nodeRefFilters.put(nodeRef, classNames); - } - boolean alreadyDisabled = classNames.contains(className); - if (!alreadyDisabled) - { - classNames.add(className); - } - return alreadyDisabled; - } - - public void enableBehaviour(QName className) - { - List classFilters = TransactionalResourceHelper.getList(KEY_CLASS_FILTER); - classFilters.remove(className); - } - - public void enableBehaviour(NodeRef nodeRef, QName className) - { - nodeRef = tenantService.getName(nodeRef); - - Map> nodeRefFilters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER); - List classNames = nodeRefFilters.get(nodeRef); - if (classNames != null) - { - classNames.remove(className); - if (classNames.size() == 0) - { - nodeRefFilters.remove(nodeRef); - } - } - } + @Deprecated + @Override + // TODO public void enableBehaviours(NodeRef nodeRef) { - nodeRef = tenantService.getName(nodeRef); - - Map> nodeRefFilters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER); - nodeRefFilters.remove(nodeRef); + enableBehaviour(nodeRef); } - + + @Deprecated + @Override + // TODO public void disableAllBehaviours() { - TransactionalResourceHelper.setBoolean(KEY_GLOBAL_FILTER); + disableBehaviour(); } - + + @Deprecated + @Override + // TODO public void enableAllBehaviours() { - TransactionalResourceHelper.resetBoolean(KEY_GLOBAL_FILTER); - - Map> filters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER); - filters.clear(); + enableBehaviour(); } - public boolean isEnabled(NodeRef nodeRef, QName className) + @Override + public void disableBehaviour() { - if(TransactionalResourceHelper.testBoolean(KEY_GLOBAL_FILTER)) + if (logger.isDebugEnabled()) { - return false; + logger.debug("Behaviour: DISABLE (" + AlfrescoTransactionSupport.getTransactionId() + "): ALL"); } - - // check global filters - if (!isEnabled(className)) + + TransactionalResourceHelper.incrementCount(KEY_FILTER_COUNT); + + TransactionalResourceHelper.incrementCount(KEY_GLOBAL_FILTERS); + + if (logger.isDebugEnabled()) { - return false; + logger.debug(" Now: " + TransactionalResourceHelper.getCount(KEY_GLOBAL_FILTERS)); + } + } + + @Override + public void disableBehaviour(QName className) + { + if (logger.isDebugEnabled()) + { + logger.debug("Behaviour: DISABLE (" + AlfrescoTransactionSupport.getTransactionId() + "): " + className); + } + ParameterCheck.mandatory("className", className); + + TransactionalResourceHelper.incrementCount(KEY_FILTER_COUNT); + + Map classFilters = TransactionalResourceHelper.getMap(KEY_CLASS_FILTERS); + MutableInt filter = classFilters.get(className); + if (filter == null) + { + filter = new MutableInt(1); // Already incremented + classFilters.put(className, filter); + } + else + { + filter.increment(); } + if (logger.isDebugEnabled()) + { + logger.debug(" Now: " + filter); + } + } + + @Override + public void disableBehaviour(NodeRef nodeRef, QName className) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("className", className); nodeRef = tenantService.getName(nodeRef); - // check node level filters - Map> filters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER); - List nodeClassFilters = filters.get(nodeRef); - if (nodeClassFilters != null) + if (logger.isDebugEnabled()) { - boolean filtered = nodeClassFilters.contains(className); - if (filtered) - { - return false; - } - for (QName filterName : nodeClassFilters) - { - filtered = dictionaryService.isSubClass(className, filterName); - if (filtered) - { - return false; - } - } + logger.debug("Behaviour: DISABLE (" + AlfrescoTransactionSupport.getTransactionId() + "): " + nodeRef + "/" + className); } + nodeRef = tenantService.getName(nodeRef); - return true; + TransactionalResourceHelper.incrementCount(KEY_FILTER_COUNT); + + Map> instanceClassFilters = TransactionalResourceHelper.getMap(KEY_INSTANCE_CLASS_FILTERS); + Map classFilters = instanceClassFilters.get(nodeRef); + if (classFilters == null) + { + classFilters = new HashMap(3); + instanceClassFilters.put(nodeRef, classFilters); + } + MutableInt filter = classFilters.get(className); + if (filter == null) + { + filter = new MutableInt(0); + classFilters.put(className, filter); + } + filter.increment(); + + if (logger.isDebugEnabled()) + { + logger.debug(" Now: " + filter); + } } + @Override + public void disableBehaviour(NodeRef nodeRef) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + + if (logger.isDebugEnabled()) + { + logger.debug("Behaviour: DISABLE (" + AlfrescoTransactionSupport.getTransactionId() + "): " + nodeRef + "/ALL:"); + } + nodeRef = tenantService.getName(nodeRef); + + TransactionalResourceHelper.incrementCount(KEY_FILTER_COUNT); + + Map instanceFilters = TransactionalResourceHelper.getMap(KEY_INSTANCE_FILTERS); + MutableInt filter = instanceFilters.get(nodeRef); + if (filter == null) + { + filter = new MutableInt(0); + instanceFilters.put(nodeRef, filter); + } + filter.increment(); + + if (logger.isDebugEnabled()) + { + logger.debug(" Now:" + filter); + } + } + + @Override + public void enableBehaviour() + { + if (logger.isDebugEnabled()) + { + logger.debug("Behaviour: ENABLE (" + AlfrescoTransactionSupport.getTransactionId() + "): ALL"); + } + + TransactionalResourceHelper.decrementCount(KEY_FILTER_COUNT, false); + + TransactionalResourceHelper.decrementCount(KEY_GLOBAL_FILTERS, false); + + if (logger.isDebugEnabled()) + { + logger.debug(" Now: " + TransactionalResourceHelper.getCount(KEY_GLOBAL_FILTERS)); + } + } + + @Override + public void enableBehaviour(QName className) + { + ParameterCheck.mandatory("className", className); + + if (logger.isDebugEnabled()) + { + logger.debug("Behaviour: ENABLE (" + AlfrescoTransactionSupport.getTransactionId() + "): " + className); + } + + TransactionalResourceHelper.decrementCount(KEY_FILTER_COUNT, false); + + if (!TransactionalResourceHelper.isResourcePresent(KEY_CLASS_FILTERS)) + { + // Nothing was disabled + return; + } + Map classFilters = TransactionalResourceHelper.getMap(KEY_CLASS_FILTERS); + MutableInt filter = classFilters.get(className); + if (filter == null) + { + // Class was not disabled + return; + } + else if (filter.intValue() <= 0) + { + // Can't go below zero for this + } + else + { + filter.decrement(); + } + + if (logger.isDebugEnabled()) + { + logger.debug(" Now: "+ filter); + } + } + + @Override + public void enableBehaviour(NodeRef nodeRef, QName className) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("className", className); + + if (logger.isDebugEnabled()) + { + logger.debug("Behaviour: ENABLE (" + AlfrescoTransactionSupport.getTransactionId() + "): " + nodeRef + "/" + className); + } + + TransactionalResourceHelper.decrementCount(KEY_FILTER_COUNT, false); + + if (!TransactionalResourceHelper.isResourcePresent(KEY_INSTANCE_CLASS_FILTERS)) + { + // Nothing was disabled + return; + } + nodeRef = tenantService.getName(nodeRef); + + Map> instanceClassFilters = TransactionalResourceHelper.getMap(KEY_INSTANCE_CLASS_FILTERS); + Map classFilters = instanceClassFilters.get(nodeRef); + if (classFilters == null) + { + // Instance classes were not disabled + return; + } + MutableInt filter = classFilters.get(className); + if (filter == null) + { + // Class was not disabled + return; + } + else if (filter.intValue() <= 0) + { + // Can't go below zero for this + } + else + { + filter.decrement(); + } + + if (logger.isDebugEnabled()) + { + logger.debug(" Now: "+ filter); + } + } + + @Override + public void enableBehaviour(NodeRef nodeRef) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + + if (logger.isDebugEnabled()) + { + logger.debug("Behaviour: ENABLE (" + AlfrescoTransactionSupport.getTransactionId() + "): " + nodeRef + "/ALL"); + } + + TransactionalResourceHelper.decrementCount(KEY_FILTER_COUNT, false); + + if (!TransactionalResourceHelper.isResourcePresent(KEY_INSTANCE_FILTERS)) + { + // Nothing was disabled + return; + } + nodeRef = tenantService.getName(nodeRef); + + Map instanceFilters = TransactionalResourceHelper.getMap(KEY_INSTANCE_FILTERS); + MutableInt filter = instanceFilters.get(nodeRef); + if (filter == null) + { + // Instance was not disabled + return; + } + else if (filter.intValue() <= 0) + { + // Can't go below zero for this + } + else + { + filter.decrement(); + } + + if (logger.isDebugEnabled()) + { + logger.debug(" Now:" + filter); + } + } + + @Override + public boolean isEnabled() + { + return TransactionalResourceHelper.getCount(KEY_GLOBAL_FILTERS) <= 0; + } + + @Override public boolean isEnabled(QName className) { - if(TransactionalResourceHelper.testBoolean(KEY_GLOBAL_FILTER)) + ParameterCheck.mandatory("className", className); + + // Check the global, first + if (!isEnabled()) { return false; } - // check global class filters - List classFilters = TransactionalResourceHelper.getList(KEY_CLASS_FILTER); - boolean filtered = classFilters.contains(className); - if (filtered) + if (!TransactionalResourceHelper.isResourcePresent(KEY_CLASS_FILTERS)) + { + // Nothing was disabled + return true; + } + Map classFilters = TransactionalResourceHelper.getMap(KEY_CLASS_FILTERS); + MutableInt classFilter = classFilters.get(className); + return (classFilter == null) || classFilter.intValue() <= 0; + } + + @Override + public boolean isEnabled(NodeRef nodeRef, QName className) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("className", className); + + // Check the class (includes global) and instance, first + if (!isEnabled(className) || !isEnabled(nodeRef)) { return false; } - for (QName classFilter : classFilters) + + if (!TransactionalResourceHelper.isResourcePresent(KEY_INSTANCE_CLASS_FILTERS)) { - filtered = dictionaryService.isSubClass(className, classFilter); - if (filtered) + // Nothing was disabled + return true; + } + nodeRef = tenantService.getName(nodeRef); + + Map> instanceClassFilters = TransactionalResourceHelper.getMap(KEY_INSTANCE_CLASS_FILTERS); + Map classFilters = instanceClassFilters.get(nodeRef); + if (classFilters == null) + { + // Instance classes were not disabled + return true; + } + for (QName classCheck : classFilters.keySet()) + { + // Ignore if it is not part of the hierarchy we are requesting + if (!dictionaryService.isSubClass(className, classCheck)) { + continue; + } + MutableInt filter = classFilters.get(className); + if (filter != null && filter.intValue() > 0) + { + // Class was disabled return false; } } - return true; } - public boolean isActivated() + @Override + public boolean isEnabled(NodeRef nodeRef) { - List classFilters = TransactionalResourceHelper.getList(KEY_CLASS_FILTER); - Map> nodeRefFilters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER); - boolean globalFlag = TransactionalResourceHelper.testBoolean(KEY_GLOBAL_FILTER); - return ((!classFilters.isEmpty()) || (!nodeRefFilters.isEmpty()) || globalFlag); + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Check the class (includes global) and instance, first + if (!isEnabled()) + { + return false; + } + + if (!TransactionalResourceHelper.isResourcePresent(KEY_INSTANCE_FILTERS)) + { + // Nothing was disabled + return true; + } + nodeRef = tenantService.getName(nodeRef); + + Map instanceFilters = TransactionalResourceHelper.getMap(KEY_INSTANCE_FILTERS); + MutableInt filter = instanceFilters.get(nodeRef); + if (filter != null && filter.intValue() > 0) + { + // Instance was disabled + return false; + } + return true; } + @Override + public boolean isActivated() + { + return TransactionalResourceHelper.getCount(KEY_FILTER_COUNT) > 0; + } } diff --git a/source/java/org/alfresco/repo/policy/PolicyComponentTransactionTest.java b/source/java/org/alfresco/repo/policy/PolicyComponentTransactionTest.java index b02f6b34da..3f82b33f9a 100644 --- a/source/java/org/alfresco/repo/policy/PolicyComponentTransactionTest.java +++ b/source/java/org/alfresco/repo/policy/PolicyComponentTransactionTest.java @@ -32,6 +32,7 @@ import org.alfresco.repo.policy.Policy.Arg; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; @@ -50,6 +51,7 @@ public class PolicyComponentTransactionTest extends TestCase private static ApplicationContext applicationContext = ApplicationContextHelper.getApplicationContext(); private static ClassPolicyDelegate sideEffectDelegate = null; private PolicyComponent policyComponent; + private BehaviourFilter behaviourFilter; private TransactionService trxService; private AuthenticationComponent authenticationComponent; @@ -68,6 +70,7 @@ public class PolicyComponentTransactionTest extends TestCase // retrieve policy component this.policyComponent = (PolicyComponent)applicationContext.getBean("policyComponent"); + this.behaviourFilter = (BehaviourFilter) applicationContext.getBean("policyBehaviourFilter"); this.trxService = (TransactionService) applicationContext.getBean("transactionComponent"); this.authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent"); this.authenticationComponent.setSystemUserAsCurrentUser(); @@ -283,9 +286,100 @@ public class PolicyComponentTransactionTest extends TestCase { try { userTransaction2.rollback(); } catch (IllegalStateException ee) {} throw e; + } + + NodeRef nodeRef = new NodeRef(TEST_NAMESPACE, "test", "123"); + // Check enabling and disabling of behaviours within the transaction + // We disable multiple times and enable, including enabling when not disabled + UserTransaction userTransaction3 = trxService.getUserTransaction(); + try + { + userTransaction3.begin(); + + // Check all enabled to start + checkBehaviour(BASE_TYPE, nodeRef, true, true, true, true); + + // Check instance with nesting + behaviourFilter.enableBehaviour(nodeRef); + checkBehaviour(BASE_TYPE, nodeRef, true, true, true, true); + behaviourFilter.disableBehaviour(nodeRef); + checkBehaviour(BASE_TYPE, nodeRef, true, true, false, false); + behaviourFilter.disableBehaviour(nodeRef); + checkBehaviour(BASE_TYPE, nodeRef, true, true, false, false); + behaviourFilter.enableBehaviour(nodeRef); + checkBehaviour(BASE_TYPE, nodeRef, true, true, false, false); + behaviourFilter.enableBehaviour(nodeRef); + checkBehaviour(BASE_TYPE, nodeRef, true, true, true, true); + + // Check class and instance with nesting + behaviourFilter.enableBehaviour(nodeRef, BASE_TYPE); + checkBehaviour(BASE_TYPE, nodeRef, true, true, true, true); + behaviourFilter.disableBehaviour(nodeRef, BASE_TYPE); + checkBehaviour(BASE_TYPE, nodeRef, true, true, false, true); + behaviourFilter.disableBehaviour(nodeRef, BASE_TYPE); + checkBehaviour(BASE_TYPE, nodeRef, true, true, false, true); + behaviourFilter.enableBehaviour(nodeRef, BASE_TYPE); + checkBehaviour(BASE_TYPE, nodeRef, true, true, false, true); + behaviourFilter.enableBehaviour(nodeRef, BASE_TYPE); + checkBehaviour(BASE_TYPE, nodeRef, true, true, true, true); + + // Check class with nesting + behaviourFilter.enableBehaviour(BASE_TYPE); + checkBehaviour(BASE_TYPE, nodeRef, true, true, true, true); + behaviourFilter.disableBehaviour(BASE_TYPE); + checkBehaviour(BASE_TYPE, nodeRef, true, false, false, true); + behaviourFilter.disableBehaviour(BASE_TYPE); + checkBehaviour(BASE_TYPE, nodeRef, true, false, false, true); + behaviourFilter.enableBehaviour(BASE_TYPE); + checkBehaviour(BASE_TYPE, nodeRef, true, false, false, true); + behaviourFilter.enableBehaviour(BASE_TYPE); + checkBehaviour(BASE_TYPE, nodeRef, true, true, true, true); + + // Check global with nesting + behaviourFilter.enableBehaviour(); + checkBehaviour(BASE_TYPE, nodeRef, true, true, true, true); + behaviourFilter.disableBehaviour(); + checkBehaviour(BASE_TYPE, nodeRef, false, false, false, false); + behaviourFilter.disableBehaviour(); + checkBehaviour(BASE_TYPE, nodeRef, false, false, false, false); + behaviourFilter.enableBehaviour(); + checkBehaviour(BASE_TYPE, nodeRef, false, false, false, false); + behaviourFilter.enableBehaviour(); + checkBehaviour(BASE_TYPE, nodeRef, true, true, true, true); + + userTransaction3.commit(); + } + catch(Exception e) + { + try { userTransaction3.rollback(); } catch (IllegalStateException ee) {} + throw e; } } + /** + * @param className the class to check + * @param nodeRef the node instance to check + * @param globalEnabled true if the global filter should be enabled + * @param classEnabled true if the class should be enabled + * @param classInstanceEnabled true if the class and instance should be enabled + * @param instanceEnabled true if the instance should be enabled + */ + private void checkBehaviour( + QName className, NodeRef nodeRef, + boolean globalEnabled, + boolean classEnabled, + boolean classInstanceEnabled, + boolean instanceEnabled) + + { + assertEquals("Incorrect behaviour state: global: ", globalEnabled, behaviourFilter.isEnabled()); + assertEquals("Incorrect behaviour state: class: ", classEnabled, behaviourFilter.isEnabled(className)); + assertEquals("Incorrect behaviour state: classAndInstance", classInstanceEnabled, behaviourFilter.isEnabled(nodeRef, className)); + assertEquals("Incorrect behaviour state: instance", instanceEnabled, behaviourFilter.isEnabled(nodeRef)); + assertEquals("'Active' flag incorrect: ", + !(globalEnabled && classEnabled && classInstanceEnabled && instanceEnabled), + behaviourFilter.isActivated()); + } // // Behaviour Implementations diff --git a/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java b/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java index 42b47ffb1c..91dabc4a11 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java +++ b/source/java/org/alfresco/repo/publishing/PublishingEventProcessor.java @@ -73,13 +73,13 @@ public class PublishingEventProcessor { try { - behaviourFilter.disableAllBehaviours(); + behaviourFilter.disableBehaviour(); channel.publishEvent(event); sendStatusUpdate(channel, event.getStatusUpdate()); } finally { - behaviourFilter.enableAllBehaviours(); + behaviourFilter.enableBehaviour(); } return null; } diff --git a/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java b/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java index cdf165f28a..b379c32085 100644 --- a/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java +++ b/source/java/org/alfresco/repo/tagging/UpdateTagScopesActionExecuter.java @@ -378,7 +378,7 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase // Changing the tag scope values is a system operation // As such, don't fire policies/behaviours during this - behaviourFilter.disableAllBehaviours(); + behaviourFilter.disableBehaviour(); // Get the current tags ContentReader contentReader = contentService.getReader(tagScopeNode, ContentModel.PROP_TAGSCOPE_CACHE); @@ -452,7 +452,7 @@ public class UpdateTagScopesActionExecuter extends ActionExecuterAbstractBase // We're done making our changes // Allow behaviours to fire again if they want to - behaviourFilter.enableAllBehaviours(); + behaviourFilter.enableBehaviour(); // Log this if required if(logger.isDebugEnabled()) diff --git a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java index 8e794ca6d1..fe772936e5 100644 --- a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java +++ b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java @@ -277,11 +277,27 @@ public class AlfrescoTransactionSupportTest extends TestCase { public Object execute() throws Exception { + // Check map access Map map = TransactionalResourceHelper.getMap("abc"); assertNotNull("Map not created", map); map.put("1", "ONE"); Map mapCheck = TransactionalResourceHelper.getMap("abc"); assertTrue("Same map not retrieved", map == mapCheck); + // Check counter + assertEquals("Transactional count incorrect. ", 0, TransactionalResourceHelper.getCount("myCount")); + assertEquals("Transactional count incorrect. ", -1, TransactionalResourceHelper.decrementCount("myCount", true)); + assertEquals("Transactional count incorrect. ", -2, TransactionalResourceHelper.decrementCount("myCount", true)); + assertEquals("Transactional count incorrect. ", -2, TransactionalResourceHelper.getCount("myCount")); + assertEquals("Transactional count incorrect. ", -1, TransactionalResourceHelper.incrementCount("myCount")); + assertEquals("Transactional count incorrect. ", 0, TransactionalResourceHelper.incrementCount("myCount")); + assertEquals("Transactional count incorrect. ", 1, TransactionalResourceHelper.incrementCount("myCount")); + assertEquals("Transactional count incorrect. ", 1, TransactionalResourceHelper.getCount("myCount")); + assertEquals("Transactional count incorrect. ", 1, TransactionalResourceHelper.getCount("myCount")); + TransactionalResourceHelper.resetCount("myCount"); + assertEquals("Transactional count incorrect. ", 0, TransactionalResourceHelper.getCount("myCount")); + assertEquals("Transactional count incorrect. ", 0, TransactionalResourceHelper.decrementCount("myCount", false)); + assertEquals("Transactional count incorrect. ", 0, TransactionalResourceHelper.decrementCount("myCount", false)); + // Done return null; } }; diff --git a/source/java/org/alfresco/repo/transaction/TransactionalResourceHelper.java b/source/java/org/alfresco/repo/transaction/TransactionalResourceHelper.java index 6aa98efc11..d0b212fa31 100644 --- a/source/java/org/alfresco/repo/transaction/TransactionalResourceHelper.java +++ b/source/java/org/alfresco/repo/transaction/TransactionalResourceHelper.java @@ -26,6 +26,8 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import org.apache.commons.lang.mutable.MutableInt; + /** * Helper class that will look up or create transactional resources. * This shortcuts some of the "if not existing, then create" code. @@ -35,6 +37,83 @@ import java.util.TreeSet; */ public abstract class TransactionalResourceHelper { + /** + * Get the current count value for a named key + * + * @param resourceKey the key to count against + * @return the current value for the named key + */ + public static final int getCount(Object resourceKey) + { + MutableInt counter = (MutableInt) AlfrescoTransactionSupport.getResource(resourceKey); + return counter == null ? 0 : counter.intValue(); + } + + /** + * Reset the current count value for a named key. After this operation, the effective + * value will be 0. + * + * @param resourceKey the key to count against + */ + public static final void resetCount(Object resourceKey) + { + AlfrescoTransactionSupport.unbindResource(resourceKey); + } + + /** + * Increment a count value for named key + * + * @param resourceKey the key to count against + * @return the newly-incremented value + */ + public static final int incrementCount(Object resourceKey) + { + MutableInt counter = (MutableInt) AlfrescoTransactionSupport.getResource(resourceKey); + if (counter == null) + { + counter = new MutableInt(0); + AlfrescoTransactionSupport.bindResource(resourceKey, counter); + } + counter.increment(); + return counter.intValue(); + } + + /** + * Decrement a count value for a named key + * + * @param resourceKey the key to count against + * @param allowNegative true to allow negative values otherwise zero will be the floor + * @return the newly-decremented value (negative, if allowed) + */ + public static final int decrementCount(Object resourceKey, boolean allowNegative) + { + MutableInt counter = (MutableInt) AlfrescoTransactionSupport.getResource(resourceKey); + if (counter == null) + { + counter = new MutableInt(0); + AlfrescoTransactionSupport.bindResource(resourceKey, counter); + } + if (counter.intValue() > 0 || allowNegative) + { + counter.decrement(); + } + return counter.intValue(); + } + + /** + * Support method to determine if there is already a resource associated with the + * given key. This method allows quick conditional checking of the key without + * building a new collection. + * + * @param resourceKey the key of the resource to check + * @return true if a resource is already present at the key + */ + public static final boolean isResourcePresent(Object resourceKey) + { + Object resource = AlfrescoTransactionSupport.getResource(resourceKey); + return resource != null; + } + /** * Support method to retrieve or create and bind a HashMap to the current transaction. * @@ -107,52 +186,4 @@ public abstract class TransactionalResourceHelper } return list; } - - /** - * Support method to set a boolean (true) value in the current transaction. - * @param resourceKey the key under which the resource will be stored - * @return true - the value of resourceKey, was set to true, false - the value was already true - */ - public static final boolean setBoolean(Object resourceKey) - { - Boolean value = AlfrescoTransactionSupport.getResource(resourceKey); - if(value == null) - { - AlfrescoTransactionSupport.bindResource(resourceKey, Boolean.TRUE); - return true; - } - - return false; - } - - /** - * Support method to reset (make false) a boolean value in the current transaction. - * @param resourceKey the key under which the resource is stored. - */ - public static final void resetBoolean(Object resourceKey) - { - Boolean value = AlfrescoTransactionSupport.getResource(resourceKey); - if(value == null) - { - AlfrescoTransactionSupport.unbindResource(resourceKey); - } - } - - /** - * Is there a boolean value in the current transaction - * @param resourceKey the key under which the resource will be stored - * @return true - thre is, false no. - */ - public static final boolean testBoolean(Object resourceKey) - { - Boolean value = AlfrescoTransactionSupport.getResource(resourceKey); - if(value == null) - { - return false; - } - else - { - return true; - } - } } diff --git a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java b/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java index 04d3d90ac6..4682865498 100644 --- a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java +++ b/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java @@ -901,7 +901,7 @@ public class RepoTransferReceiverImpl implements TransferReceiver, XMLTransferManifestReader reader = new XMLTransferManifestReader(processor); //behaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); - behaviourFilter.disableAllBehaviours(); + behaviourFilter.disableBehaviour(); try { @@ -909,8 +909,7 @@ public class RepoTransferReceiverImpl implements TransferReceiver, } finally { - // behaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE); - behaviourFilter.enableAllBehaviours(); + behaviourFilter.enableBehaviour(); } parser.reset(); } diff --git a/source/java/org/alfresco/repo/version/Version2ServiceImpl.java b/source/java/org/alfresco/repo/version/Version2ServiceImpl.java index 960df9f4a6..605814319d 100644 --- a/source/java/org/alfresco/repo/version/Version2ServiceImpl.java +++ b/source/java/org/alfresco/repo/version/Version2ServiceImpl.java @@ -208,11 +208,7 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe // of the version label, to affect the auditable properties on the node that // is being versioned. // So, disable the auditable aspect on that node for now - boolean disableAuditable = policyBehaviourFilter.isEnabled(ContentModel.ASPECT_AUDITABLE); - if(disableAuditable) - { - policyBehaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); - } + policyBehaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); // If the version aspect is not there then add it to the 'live' (versioned) node if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false) @@ -364,10 +360,7 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe version.getVersionLabel()); // Re-enable the auditable aspect (if we turned it off earlier) - if(disableAuditable) - { - policyBehaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); - } + policyBehaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); // Invoke the policy behaviour invokeAfterCreateVersion(nodeRef, version);