diff --git a/config/alfresco/rule-services-context.xml b/config/alfresco/rule-services-context.xml index fe4077c3ae..79955d57a9 100644 --- a/config/alfresco/rule-services-context.xml +++ b/config/alfresco/rule-services-context.xml @@ -18,12 +18,12 @@ - - - + + false + @@ -81,6 +81,7 @@ + @@ -131,7 +132,19 @@ - onDeleteChildAssociation + beforeDeleteChildAssociation + + + true + + + + + + beforeDeleteNode + + + true diff --git a/source/java/org/alfresco/repo/action/ActionServiceImplTest.java b/source/java/org/alfresco/repo/action/ActionServiceImplTest.java index 1d57a7eeba..fda3864dc0 100644 --- a/source/java/org/alfresco/repo/action/ActionServiceImplTest.java +++ b/source/java/org/alfresco/repo/action/ActionServiceImplTest.java @@ -330,7 +330,6 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest // Edit the properties of the action Map properties = new HashMap(1); properties.put(ContentModel.PROP_NAME, "testName"); - action.setParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_PROPERTIES, (Serializable)properties); action.setParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, ContentModel.ASPECT_AUDITABLE); // Set the compensating action @@ -342,12 +341,8 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest Action savedAction2 = this.actionService.getAction(this.nodeRef, actionId); // Check the updated properties - assertEquals(2, savedAction2.getParameterValues().size()); + assertEquals(1, savedAction2.getParameterValues().size()); assertEquals(ContentModel.ASPECT_AUDITABLE, savedAction2.getParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_NAME)); - Map temp = (Map)savedAction2.getParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_PROPERTIES); - assertNotNull(temp); - assertEquals(1, temp.size()); - assertEquals("testName", temp.get(ContentModel.PROP_NAME)); // Check the compensating action Action savedCompensatingAction = savedAction2.getCompensatingAction(); @@ -393,14 +388,8 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest // Save the action this.actionService.saveAction(this.nodeRef, action); - // Check the owning node ref - //assertEquals(this.nodeRef, action.getOwningNodeRef()); - // Get the action - Action savedAction = this.actionService.getAction(this.nodeRef, actionId); - - // Check the owning node ref - //assertEquals(this.nodeRef, savedAction.getOwningNodeRef());; + this.actionService.getAction(this.nodeRef, actionId); } /** @@ -414,9 +403,6 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest // Set the parameters of the action action.setParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, ContentModel.ASPECT_VERSIONABLE); - Map properties = new HashMap(1); - properties.put(ContentModel.PROP_NAME, "testName"); - action.setParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_PROPERTIES, (Serializable)properties); // Set the conditions of the action ActionCondition actionCondition = this.actionService.createActionCondition(NoConditionEvaluator.NAME); @@ -439,10 +425,6 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest // Check the properties assertEquals(action.getParameterValues().size(), savedAction.getParameterValues().size()); assertEquals(ContentModel.ASPECT_VERSIONABLE, savedAction.getParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_NAME)); - Map temp = (Map)savedAction.getParameterValue(AddFeaturesActionExecuter.PARAM_ASPECT_PROPERTIES); - assertNotNull(temp); - assertEquals(1, temp.size()); - assertEquals("testName", temp.get(ContentModel.PROP_NAME)); // Check the conditions assertNotNull(savedAction.getActionConditions()); diff --git a/source/java/org/alfresco/repo/action/executer/AddFeaturesActionExecuter.java b/source/java/org/alfresco/repo/action/executer/AddFeaturesActionExecuter.java index 13a8368083..73c6976f9c 100644 --- a/source/java/org/alfresco/repo/action/executer/AddFeaturesActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/AddFeaturesActionExecuter.java @@ -41,7 +41,6 @@ public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase */ public static final String NAME = "add-features"; public static final String PARAM_ASPECT_NAME = "aspect-name"; - public static final String PARAM_ASPECT_PROPERTIES = "aspect_properties"; /** * The node service diff --git a/source/java/org/alfresco/repo/rule/BaseRuleTest.java b/source/java/org/alfresco/repo/rule/BaseRuleTest.java index ef176ab7fa..332f0495dd 100644 --- a/source/java/org/alfresco/repo/rule/BaseRuleTest.java +++ b/source/java/org/alfresco/repo/rule/BaseRuleTest.java @@ -156,6 +156,11 @@ public class BaseRuleTest extends BaseSpringTest } protected Rule createTestRule(boolean isAppliedToChildren) + { + return createTestRule(isAppliedToChildren, TITLE); + } + + protected Rule createTestRule(boolean isAppliedToChildren, String title) { // Rule properties Map conditionProps = new HashMap(); @@ -178,7 +183,7 @@ public class BaseRuleTest extends BaseSpringTest // Create the rule Rule rule = new Rule(); rule.setRuleTypes(ruleTypes); - rule.setTitle(TITLE); + rule.setTitle(title); rule.setDescription(DESCRIPTION); rule.applyToChildren(isAppliedToChildren); rule.setAction(action); diff --git a/source/java/org/alfresco/repo/rule/RuleModel.java b/source/java/org/alfresco/repo/rule/RuleModel.java index 125efcc03e..bb0d318cfd 100644 --- a/source/java/org/alfresco/repo/rule/RuleModel.java +++ b/source/java/org/alfresco/repo/rule/RuleModel.java @@ -18,7 +18,10 @@ public interface RuleModel static final QName PROP_APPLY_TO_CHILDREN = QName.createQName(RULE_MODEL_URI, "applyToChildren"); static final QName PROP_EXECUTE_ASYNC = QName.createQName(RULE_MODEL_URI, "executeAsynchronously"); static final QName ASSOC_ACTION = QName.createQName(RULE_MODEL_URI, "action"); + static final QName PROP_DISABLED = QName.createQName(RULE_MODEL_URI, "disabled"); static final QName ASPECT_RULES = QName.createQName(RULE_MODEL_URI, "rules"); static final QName ASSOC_RULE_FOLDER = QName.createQName(RULE_MODEL_URI, "ruleFolder"); + + static final QName ASPECT_IGNORE_INHERITED_RULES = QName.createQName(RULE_MODEL_URI, "ignoreInheritedRules"); } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java b/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java index 6fe7325ab5..c00ed1e840 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java @@ -85,9 +85,7 @@ import org.springframework.util.StopWatch; */ public class RuleServiceCoverageTest extends TestCase { - //private static final ContentData CONTENT_DATA_TEXT = new ContentData(null, MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, "UTF-8"); - - /** + /** * Application context used during the test */ static ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:alfresco/application-context.xml"); @@ -343,6 +341,46 @@ public class RuleServiceCoverageTest extends TestCase // System.out.println(NodeStoreInspector.dumpNodeStore(this.nodeService, this.testStoreRef)); } + public void testDisableIndividualRules() + { + Map params = new HashMap(1); + params.put("aspect-name", ContentModel.ASPECT_CONFIGURABLE); + + Rule rule = createRule( + RuleType.INBOUND, + AddFeaturesActionExecuter.NAME, + params, + NoConditionEvaluator.NAME, + null); + rule.setRuleDisabled(true); + + this.ruleService.saveRule(this.nodeRef, rule); + + NodeRef newNodeRef = this.nodeService.createNode( + this.nodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(TEST_NAMESPACE, "children"), + ContentModel.TYPE_CONTENT, + getContentProperties()).getChildRef(); + addContentToNode(newNodeRef); + assertFalse(this.nodeService.hasAspect(newNodeRef, ContentModel.ASPECT_CONFIGURABLE)); + + Rule rule2 = this.ruleService.getRule(rule.getNodeRef()); + rule2.setRuleDisabled(false); + this.ruleService.saveRule(this.nodeRef, rule2); + + // Re-try the test now the rule has been re-enabled + NodeRef newNodeRef2 = this.nodeService.createNode( + this.nodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(TEST_NAMESPACE, "children"), + ContentModel.TYPE_CONTENT, + getContentProperties()).getChildRef(); + addContentToNode(newNodeRef2); + assertTrue(this.nodeService.hasAspect(newNodeRef2, ContentModel.ASPECT_CONFIGURABLE)); + + } + public void testDisableRule() { this.nodeService.addAspect(this.nodeRef, ContentModel.ASPECT_LOCKABLE, null); @@ -1597,36 +1635,8 @@ public class RuleServiceCoverageTest extends TestCase tx.commit(); - Thread.sleep(10000); - //System.out.println(NodeStoreInspector.dumpNodeStore(this.nodeService, this.testStoreRef)); - - //AuthenticationComponent authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent"); - //authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName()); - - // Check that the created node is still there - //List origRefs = this.nodeService.getChildAssocs( - // this.nodeRef, - // RegexQNamePattern.MATCH_ALL, QName.createQName(TEST_NAMESPACE, "origional")); - //assertNotNull(origRefs); - //assertEquals(1, origRefs.size()); - //NodeRef origNodeRef = origRefs.get(0).getChildRef(); - //assertEquals(newNodeRef, origNodeRef); - - // Check that the created node has been copied - //List copyChildAssocRefs = this.nodeService.getChildAssocs( - // this.rootNodeRef, - // RegexQNamePattern.MATCH_ALL, QName.createQName(TEST_NAMESPACE, "transformed")); - //assertNotNull(copyChildAssocRefs); - //assertEquals(1, copyChildAssocRefs.size()); - //NodeRef copyNodeRef = copyChildAssocRefs.get(0).getChildRef(); - //assertTrue(this.nodeService.hasAspect(copyNodeRef, ContentModel.ASPECT_COPIEDFROM)); - //NodeRef source = (NodeRef)this.nodeService.getProperty(copyNodeRef, ContentModel.PROP_COPY_REFERENCE); - //assertEquals(newNodeRef, source); - - // Check the transformed content - //ContentData contentData = (ContentData) nodeService.getProperty(copyNodeRef, ContentModel.PROP_CONTENT); - //assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, contentData.getMimetype()); - + // Sleep to ensure work is done b4 execution is canceled + Thread.sleep(10000); } catch (Exception exception) { diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java index 8f92e35eb6..2cb314b3f0 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java @@ -40,7 +40,6 @@ import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.rule.RuleServiceException; import org.alfresco.service.cmr.rule.RuleType; -import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.GUID; @@ -88,11 +87,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService */ private ActionService actionService; - /** - * The search service - */ - private SearchService searchService; - /** * The dictionary service */ @@ -126,10 +120,15 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService private TransactionListener ruleTransactionListener = new RuleTransactionListener(this); /** - * Indicates whether the rules are disabled for the curren thread + * Indicates whether the rules are disabled for the current thread */ private ThreadLocal rulesDisabled = new ThreadLocal(); + /** + * Global flag that indicates whether the + */ + private boolean globalRulesDisabled = false; + /** * Set the permission-safe node service * @@ -170,16 +169,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService this.runtimeActionService = runtimeActionService; } - /** - * Set the search service - * - * @param searchService the search service - */ - public void setSearchService(SearchService searchService) - { - this.searchService = searchService; - } - /** * Set the dictionary service * @@ -189,6 +178,16 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService { this.dictionaryService = dictionaryService; } + + /** + * Set the global rules disabled flag + * + * @param rulesDisabled true to disable allr ules, false otherwise + */ + public void setRulesDisabled(boolean rulesDisabled) + { + this.globalRulesDisabled = rulesDisabled; + } /** * Gets the saved rule folder reference @@ -253,7 +252,7 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService */ public boolean isEnabled() { - return (this.rulesDisabled.get() == null); + return (this.globalRulesDisabled == false && this.rulesDisabled.get() == null); } /** @@ -331,7 +330,7 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService if (this.runtimeNodeService.exists(nodeRef) == true && checkNodeType(nodeRef) == true) { - if (includeInherited == true) + if (includeInherited == true && this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false) { // Get any inherited rules for (Rule rule : getInheritedRules(nodeRef, ruleTypeName, null)) @@ -441,58 +440,62 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService private List getInheritedRules(NodeRef nodeRef, String ruleTypeName, Set visitedNodeRefs) { List inheritedRules = new ArrayList(); - - // Create the visited nodes set if it has not already been created - if (visitedNodeRefs == null) - { - visitedNodeRefs = new HashSet(); - } - - // This check prevents stack over flow when we have a cyclic node graph - if (visitedNodeRefs.contains(nodeRef) == false) - { - visitedNodeRefs.add(nodeRef); - - List allInheritedRules = new ArrayList(); - List parents = this.runtimeNodeService.getParentAssocs(nodeRef); - for (ChildAssociationRef parent : parents) - { - List rules = getRules(parent.getParentRef(), false); - for (Rule rule : rules) - { - // Add is we hanvn't already added and it should be applied to the children - if (rule.isAppliedToChildren() == true && allInheritedRules.contains(rule) == false) - { - allInheritedRules.add(rule); - } - } - - for (Rule rule : getInheritedRules(parent.getParentRef(), ruleTypeName, visitedNodeRefs)) - { - // Ensure that we don't get any rule duplication (don't use a set cos we want to preserve order) - if (allInheritedRules.contains(rule) == false) - { - allInheritedRules.add(rule); - } - } - } - - if (ruleTypeName == null) - { - inheritedRules = allInheritedRules; - } - else - { - // Filter the rule list by rule type - for (Rule rule : allInheritedRules) - { - if (rule.getRuleTypes().contains(ruleTypeName) == true) - { - inheritedRules.add(rule); - } - } - } - } + + if (this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false) + { + // Create the visited nodes set if it has not already been created + if (visitedNodeRefs == null) + { + visitedNodeRefs = new HashSet(); + } + + // This check prevents stack over flow when we have a cyclic node graph + if (visitedNodeRefs.contains(nodeRef) == false) + { + visitedNodeRefs.add(nodeRef); + + List allInheritedRules = new ArrayList(); + List parents = this.runtimeNodeService.getParentAssocs(nodeRef); + for (ChildAssociationRef parent : parents) + { + // Add the inherited rule first + for (Rule rule : getInheritedRules(parent.getParentRef(), ruleTypeName, visitedNodeRefs)) + { + // Ensure that we don't get any rule duplication (don't use a set cos we want to preserve order) + if (allInheritedRules.contains(rule) == false) + { + allInheritedRules.add(rule); + } + } + + List rules = getRules(parent.getParentRef(), false); + for (Rule rule : rules) + { + // Add is we hanvn't already added and it should be applied to the children + if (rule.isAppliedToChildren() == true && allInheritedRules.contains(rule) == false) + { + allInheritedRules.add(rule); + } + } + } + + if (ruleTypeName == null) + { + inheritedRules = allInheritedRules; + } + else + { + // Filter the rule list by rule type + for (Rule rule : allInheritedRules) + { + if (rule.getRuleTypes().contains(ruleTypeName) == true) + { + inheritedRules.add(rule); + } + } + } + } + } return inheritedRules; } @@ -511,9 +514,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService // Create the rule Rule rule = new Rule(ruleNodeRef); - // Set the owning node ref - //rule.setOwningNodeRef((NodeRef)props.get(RuleModel.PROP_OWNING_NODEREF)); - // Set the title and description rule.setTitle((String)props.get(ContentModel.PROP_TITLE)); rule.setDescription((String)props.get(ContentModel.PROP_DESCRIPTION)); @@ -538,6 +538,15 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService executeAsync = value2.booleanValue(); } rule.setExecuteAsynchronously(executeAsync); + + // Set the disabled value + boolean ruleDisabled = false; + Boolean value3 = (Boolean)props.get(RuleModel.PROP_DISABLED); + if (value3 != null) + { + ruleDisabled = value3.booleanValue(); + } + rule.setRuleDisabled(ruleDisabled); // Get the action node reference List actions = this.nodeService.getChildAssocs(ruleNodeRef, RuleModel.ASSOC_ACTION, RuleModel.ASSOC_ACTION); @@ -597,6 +606,7 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_RULE_TYPE, (Serializable)rule.getRuleTypes()); this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_APPLY_TO_CHILDREN, rule.isAppliedToChildren()); this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_EXECUTE_ASYNC, rule.getExecuteAsynchronously()); + this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_DISABLED, rule.getRuleDisabled()); // Save the rule's action saveAction(ruleNodeRef, rule); @@ -607,6 +617,12 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService } } + /** + * Save the action related to the rule. + * + * @param ruleNodeRef the node reference representing the rule + * @param rule the rule + */ private void saveAction(NodeRef ruleNodeRef, Rule rule) { // Get the action definition from the rule @@ -708,18 +724,18 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService public void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule, boolean executeAtEnd) { // First check to see if the node has been disabled - if (this.rulesDisabled.get() == null && + if (this.isEnabled() == true && this.disabledNodeRefs.contains(this.getOwningNodeRef(rule)) == false && this.disabledRules.contains(rule) == false) { PendingRuleData pendingRuleData = new PendingRuleData(actionableNodeRef, actionedUponNodeRef, rule, executeAtEnd); - Set pendingRules = - (Set) AlfrescoTransactionSupport.getResource(KEY_RULES_PENDING); + List pendingRules = + (List) AlfrescoTransactionSupport.getResource(KEY_RULES_PENDING); if (pendingRules == null) { // bind pending rules to the current transaction - pendingRules = new HashSet(); + pendingRules = new ArrayList(); AlfrescoTransactionSupport.bindResource(KEY_RULES_PENDING, pendingRules); // bind the rule transaction listener AlfrescoTransactionSupport.bindListener(this.ruleTransactionListener); @@ -730,8 +746,11 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService } } - // Prevent the same rule being executed more than once in the same transaction - pendingRules.add(pendingRuleData); + // Prevent the same rule being executed more than once in the same transaction + if (pendingRules.contains(pendingRuleData) == false) + { + pendingRules.add(pendingRuleData); + } } else { @@ -773,8 +792,8 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService private void executePendingRulesImpl(List executeAtEndRules) { // get the transaction-local rules to execute - Set pendingRules = - (Set) AlfrescoTransactionSupport.getResource(KEY_RULES_PENDING); + List pendingRules = + (List) AlfrescoTransactionSupport.getResource(KEY_RULES_PENDING); // only execute if there are rules present if (pendingRules != null && !pendingRules.isEmpty()) { @@ -815,29 +834,41 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService if (executedRules == null || canExecuteRule(executedRules, actionedUponNodeRef, rule) == true) { - Action action = rule.getAction(); - if (action == null) - { - throw new RuleServiceException("Attempting to execute a rule that does not have a rule specified."); - } - - // Evaluate the condition - if (this.actionService.evaluateAction(action, actionedUponNodeRef) == true) - { - // Add the rule to the executed rule list - // (do this before this is executed to prevent rules being added to the pending list) - executedRules.add(new ExecutedRuleData(actionedUponNodeRef, rule)); - if (logger.isDebugEnabled() == true) - { - logger.debug(" ... Adding rule (" + rule.getTitle() + ") and nodeRef (" + actionedUponNodeRef.getId() + ") to executed list"); - } - - // Execute the rule - boolean executeAsync = rule.getExecuteAsynchronously(); - this.actionService.executeAction(action, actionedUponNodeRef, true, executeAsync); - } + executeRule(rule, actionedUponNodeRef, executedRules); } } + + /** + * @see org.alfresco.repo.rule.RuntimeRuleService#executeRule(org.alfresco.service.cmr.rule.Rule, org.alfresco.service.cmr.repository.NodeRef, java.util.Set) + */ + public void executeRule(Rule rule, NodeRef actionedUponNodeRef, Set executedRules) + { + // Get the action associated with the rule + Action action = rule.getAction(); + if (action == null) + { + throw new RuleServiceException("Attempting to execute a rule that does not have a rule specified."); + } + + // Evaluate the condition + if (this.actionService.evaluateAction(action, actionedUponNodeRef) == true) + { + if (executedRules != null) + { + // Add the rule to the executed rule list + // (do this before this is executed to prevent rules being added to the pending list) + executedRules.add(new ExecutedRuleData(actionedUponNodeRef, rule)); + if (logger.isDebugEnabled() == true) + { + logger.debug(" ... Adding rule (" + rule.getTitle() + ") and nodeRef (" + actionedUponNodeRef.getId() + ") to executed list"); + } + } + + // Execute the rule + boolean executeAsync = rule.getExecuteAsynchronously(); + this.actionService.executeAction(action, actionedUponNodeRef, true, executeAsync); + } + } /** * Determines whether the rule can be executed diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java b/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java index 10e8134e00..10f34d4820 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java @@ -148,6 +148,127 @@ public class RuleServiceImplTest extends BaseRuleTest assertEquals(1, conditions.size()); } + /** Ensure the rules are retrieved in the correct order **/ + public void testGetRulesOrder() + { + for (int index = 0; index < 10; index++) + { + Rule newRule = createTestRule(true, Integer.toString(index)); + this.ruleService.saveRule(this.nodeRef, newRule); + } + + // Check that they are all returned in the correct order + List rules = this.ruleService.getRules(this.nodeRef); + int index = 0; + for (Rule rule : rules) + { + assertEquals(Integer.toString(index), rule.getTitle()); + index++; + } + + // Create a child node + NodeRef level1 = createNewNode(this.nodeRef); + for (int index2 = 10; index2 < 20; index2++) + { + Rule newRule = createTestRule(true, Integer.toString(index2)); + this.ruleService.saveRule(level1, newRule); + } + + // Check that they are all returned in the correct order + List rules2 = this.ruleService.getRules(level1); + int index2 = 0; + for (Rule rule : rules2) + { + assertEquals(Integer.toString(index2), rule.getTitle()); + index2++; + } + + // Create a child node + NodeRef level2 = createNewNode(level1); + for (int index3 = 20; index3 < 30; index3++) + { + Rule newRule = createTestRule(true, Integer.toString(index3)); + this.ruleService.saveRule(level2, newRule); + } + + // Check that they are all returned in the correct order + List rules3 = this.ruleService.getRules(level2); + int index3 = 0; + for (Rule rule : rules3) + { + //System.out.println(rule.getTitle()); + assertEquals(Integer.toString(index3), rule.getTitle()); + index3++; + } + + // Update a couple of the rules + Rule rule1 = rules3.get(2); + rule1.setDescription("This has been changed"); + this.ruleService.saveRule(this.nodeRef, rule1); + Rule rule2 = rules3.get(12); + rule2.setDescription("This has been changed"); + this.ruleService.saveRule(level1, rule2); + Rule rule3 = rules3.get(22); + rule3.setDescription("This has been changed"); + this.ruleService.saveRule(level2, rule3); + + // Check that they are all returned in the correct order + List rules4 = this.ruleService.getRules(level2); + int index4 = 0; + for (Rule rule : rules4) + { + assertEquals(Integer.toString(index4), rule.getTitle()); + index4++; + } + } + + public void testIgnoreInheritedRules() + { + // Create the nodes and rules + this.ruleService.saveRule(this.nodeRef, createTestRule(true, "rule1")); + this.ruleService.saveRule(this.nodeRef, createTestRule(false, "rule2")); + NodeRef nodeRef1 = createNewNode(this.nodeRef); + this.ruleService.saveRule(nodeRef1, createTestRule(true, "rule3")); + this.ruleService.saveRule(nodeRef1, createTestRule(false, "rule4")); + NodeRef nodeRef2 = createNewNode(nodeRef1); + this.ruleService.saveRule(nodeRef2, createTestRule(true, "rule5")); + this.ruleService.saveRule(nodeRef2, createTestRule(false, "rule6")); + + // Apply the ignore aspect + this.nodeService.addAspect(nodeRef1, RuleModel.ASPECT_IGNORE_INHERITED_RULES, null); + + // Get the rules + List rules1 = this.ruleService.getRules(nodeRef2); + assertNotNull(rules1); + assertEquals(3, rules1.size()); + assertEquals("rule3", rules1.get(0).getTitle()); + assertEquals("rule5", rules1.get(1).getTitle()); + assertEquals("rule6", rules1.get(2).getTitle()); + + // Apply the ignore aspect + this.nodeService.addAspect(nodeRef2, RuleModel.ASPECT_IGNORE_INHERITED_RULES, null); + + // Get the rules + List rules2 = this.ruleService.getRules(nodeRef2); + assertNotNull(rules2); + assertEquals(2, rules2.size()); + assertEquals("rule5", rules2.get(0).getTitle()); + assertEquals("rule6", rules2.get(1).getTitle()); + + // Remove the ignore aspect + this.nodeService.removeAspect(nodeRef1, RuleModel.ASPECT_IGNORE_INHERITED_RULES); + this.nodeService.removeAspect(nodeRef2, RuleModel.ASPECT_IGNORE_INHERITED_RULES); + + // Get the rules + List rules3 = this.ruleService.getRules(nodeRef2); + assertNotNull(rules3); + assertEquals(4, rules3.size()); + assertEquals("rule1", rules3.get(0).getTitle()); + assertEquals("rule3", rules3.get(1).getTitle()); + assertEquals("rule5", rules3.get(2).getTitle()); + assertEquals("rule6", rules3.get(3).getTitle()); + } + /** * Test disabling the rules */ @@ -167,13 +288,12 @@ public class RuleServiceImplTest extends BaseRuleTest * @param parent the parent node * @param isActionable indicates whether the node is actionable or not */ - private NodeRef createNewNode(NodeRef parent, boolean isActionable) + private NodeRef createNewNode(NodeRef parent) { - NodeRef newNodeRef = this.nodeService.createNode(parent, + return this.nodeService.createNode(parent, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}testnode"), - ContentModel.TYPE_CONTAINER).getChildRef(); - return newNodeRef; + ContentModel.TYPE_CONTAINER).getChildRef(); } /** @@ -184,21 +304,21 @@ public class RuleServiceImplTest extends BaseRuleTest { // Create the nodes and rules - NodeRef rootWithRules = createNewNode(this.rootNodeRef, true); + NodeRef rootWithRules = createNewNode(this.rootNodeRef); Rule rule1 = createTestRule(); this.ruleService.saveRule(rootWithRules, rule1); Rule rule2 = createTestRule(true); this.ruleService.saveRule(rootWithRules, rule2); - NodeRef nonActionableChild = createNewNode(rootWithRules, false); + NodeRef nonActionableChild = createNewNode(rootWithRules); - NodeRef childWithRules = createNewNode(nonActionableChild, true); + NodeRef childWithRules = createNewNode(nonActionableChild); Rule rule3 = createTestRule(); this.ruleService.saveRule(childWithRules, rule3); Rule rule4 = createTestRule(true); this.ruleService.saveRule(childWithRules, rule4); - NodeRef rootWithRules2 = createNewNode(this.rootNodeRef, true); + NodeRef rootWithRules2 = createNewNode(this.rootNodeRef); this.nodeService.addChild( rootWithRules2, childWithRules, @@ -211,7 +331,7 @@ public class RuleServiceImplTest extends BaseRuleTest // Check that the rules are inherited in the correct way - List allRules = this.ruleService.getRules(childWithRules, true); + List allRules = this.ruleService.getRules(childWithRules); assertNotNull(allRules); assertEquals(4, allRules.size()); assertTrue(allRules.contains(rule2)); @@ -432,9 +552,9 @@ public class RuleServiceImplTest extends BaseRuleTest public void testCyclicGraphWithInheritedRules() throws Exception { - NodeRef nodeRef1 = createNewNode(this.rootNodeRef, true); - NodeRef nodeRef2 = createNewNode(nodeRef1, true); - NodeRef nodeRef3 = createNewNode(nodeRef2, true); + NodeRef nodeRef1 = createNewNode(this.rootNodeRef); + NodeRef nodeRef2 = createNewNode(nodeRef1); + NodeRef nodeRef3 = createNewNode(nodeRef2); try { this.nodeService.addChild(nodeRef3, nodeRef1, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}loop")); @@ -480,10 +600,10 @@ public class RuleServiceImplTest extends BaseRuleTest */ public void testRuleDuplication() { - NodeRef nodeRef1 = createNewNode(this.rootNodeRef, true); - NodeRef nodeRef2 = createNewNode(nodeRef1, true); - NodeRef nodeRef3 = createNewNode(nodeRef2, true); - NodeRef nodeRef4 = createNewNode(nodeRef1, true); + NodeRef nodeRef1 = createNewNode(this.rootNodeRef); + NodeRef nodeRef2 = createNewNode(nodeRef1); + NodeRef nodeRef3 = createNewNode(nodeRef2); + NodeRef nodeRef4 = createNewNode(nodeRef1); this.nodeService.addChild(nodeRef4, nodeRef3, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}test")); Rule rule1 = createTestRule(true); @@ -527,7 +647,7 @@ public class RuleServiceImplTest extends BaseRuleTest public void testCyclicAsyncRules() throws Exception { - NodeRef nodeRef = createNewNode(this.rootNodeRef, true); + NodeRef nodeRef = createNewNode(this.rootNodeRef); // Create the first rule diff --git a/source/java/org/alfresco/repo/rule/RuleTypeImpl.java b/source/java/org/alfresco/repo/rule/RuleTypeImpl.java index 015a6fbd4d..fdaf1bcf77 100644 --- a/source/java/org/alfresco/repo/rule/RuleTypeImpl.java +++ b/source/java/org/alfresco/repo/rule/RuleTypeImpl.java @@ -21,7 +21,6 @@ import java.util.List; import org.alfresco.i18n.I18NUtil; import org.alfresco.repo.action.CommonResourceAbstractBase; import org.alfresco.repo.rule.ruletrigger.RuleTrigger; -import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleService; @@ -99,7 +98,7 @@ public class RuleTypeImpl extends CommonResourceAbstractBase implements RuleType /** * @see org.alfresco.service.cmr.rule.RuleType#triggerRuleType(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef) */ - public void triggerRuleType(NodeRef nodeRef, NodeRef actionedUponNodeRef) + public void triggerRuleType(NodeRef nodeRef, NodeRef actionedUponNodeRef, boolean executeRuleImmediately) { if (this.ruleService.isEnabled() == true) { @@ -121,8 +120,20 @@ public class RuleTypeImpl extends CommonResourceAbstractBase implements RuleType } } - // Queue the rule to be executed at the end of the transaction (but still in the transaction) - ((RuntimeRuleService)this.ruleService).addRulePendingExecution(nodeRef, actionedUponNodeRef, rule); + // Only queue if the rule is not disabled + if (rule.getRuleDisabled() == false) + { + if (executeRuleImmediately == false) + { + // Queue the rule to be executed at the end of the transaction (but still in the transaction) + ((RuntimeRuleService)this.ruleService).addRulePendingExecution(nodeRef, actionedUponNodeRef, rule); + } + else + { + // Execute the rule now + ((RuntimeRuleService)this.ruleService).executeRule(rule, actionedUponNodeRef, null); + } + } } } else diff --git a/source/java/org/alfresco/repo/rule/RuleTypeImplTest.java b/source/java/org/alfresco/repo/rule/RuleTypeImplTest.java index 120978c00b..e1d44dd068 100644 --- a/source/java/org/alfresco/repo/rule/RuleTypeImplTest.java +++ b/source/java/org/alfresco/repo/rule/RuleTypeImplTest.java @@ -129,7 +129,7 @@ public class RuleTypeImplTest extends BaseSpringTest } @Override - public void triggerRuleType(NodeRef nodeRef, NodeRef actionedUponNodeRef) + public void triggerRuleType(NodeRef nodeRef, NodeRef actionedUponNodeRef, boolean executeRuleImmediately) { this.rulesTriggered = true; } diff --git a/source/java/org/alfresco/repo/rule/RuntimeRuleService.java b/source/java/org/alfresco/repo/rule/RuntimeRuleService.java index bf447bf10d..9cfbb649eb 100644 --- a/source/java/org/alfresco/repo/rule/RuntimeRuleService.java +++ b/source/java/org/alfresco/repo/rule/RuntimeRuleService.java @@ -16,6 +16,9 @@ */ package org.alfresco.repo.rule; +import java.util.Set; + +import org.alfresco.repo.rule.RuleServiceImpl.ExecutedRuleData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleType; @@ -25,6 +28,8 @@ import org.alfresco.service.cmr.rule.RuleType; */ public interface RuntimeRuleService { + void executeRule(Rule rule, NodeRef actionedUponNodeRef, Set executedRules); + void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule); void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule, boolean executeAtEnd); diff --git a/source/java/org/alfresco/repo/rule/ruleModel.xml b/source/java/org/alfresco/repo/rule/ruleModel.xml index cd64387663..d41bb8b0d6 100644 --- a/source/java/org/alfresco/repo/rule/ruleModel.xml +++ b/source/java/org/alfresco/repo/rule/ruleModel.xml @@ -16,7 +16,6 @@ - @@ -36,6 +35,11 @@ d:boolean true + + d:boolean + true + false + @@ -68,6 +72,10 @@ + + + Ignore Inherited Rules + diff --git a/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerAbstractBase.java b/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerAbstractBase.java index b684f496b6..bfd983b901 100644 --- a/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerAbstractBase.java +++ b/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerAbstractBase.java @@ -56,6 +56,12 @@ public abstract class RuleTriggerAbstractBase implements RuleTrigger /** The dictionary service */ protected DictionaryService dictionaryService; + /** + * Indicates whether the rule should be executed immediately or at the end of the transaction. + * By default this is false as all rules are executed at the end of the transaction. + */ + protected boolean executeRuleImmediately = false; + /** * Set the policy component * @@ -96,6 +102,17 @@ public abstract class RuleTriggerAbstractBase implements RuleTrigger this.dictionaryService = dictionaryService; } + /** + * Sets the values that indicates whether the rule should be executed immediately + * or not. + * + * @param executeRuleImmediately true execute the rule immediaely, false otherwise + */ + public void setExecuteRuleImmediately(boolean executeRuleImmediately) + { + this.executeRuleImmediately = executeRuleImmediately; + } + /** * Registration of an interested rule type */ @@ -117,7 +134,7 @@ public abstract class RuleTriggerAbstractBase implements RuleTrigger { for (RuleType ruleType : this.ruleTypes) { - ruleType.triggerRuleType(nodeRef, actionedUponNodeRef); + ruleType.triggerRuleType(nodeRef, actionedUponNodeRef, this.executeRuleImmediately); } } } diff --git a/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java b/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java index d854b4746c..725fee2833 100644 --- a/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java +++ b/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java @@ -35,7 +35,6 @@ public class RuleTriggerTest extends BaseSpringTest { private static final String ON_CREATE_NODE_TRIGGER = "on-create-node-trigger"; private static final String ON_UPDATE_NODE_TRIGGER = "on-update-node-trigger"; - private static final String ON_DELETE_NODE_TRIGGER = "on-delete-node-trigger"; private static final String ON_CREATE_CHILD_ASSOCIATION_TRIGGER = "on-create-child-association-trigger"; private static final String ON_DELETE_CHILD_ASSOCIATION_TRIGGER = "on-delete-child-association-trigger"; private static final String ON_CREATE_ASSOCIATION_TRIGGER = "on-create-association-trigger"; @@ -287,7 +286,7 @@ public class RuleTriggerTest extends BaseSpringTest return "displayLabel"; } - public void triggerRuleType(NodeRef nodeRef, NodeRef actionedUponNodeRef) + public void triggerRuleType(NodeRef nodeRef, NodeRef actionedUponNodeRef, boolean executeRuleImmediately) { // Indicate that the rules have been triggered this.rulesTriggered = true; diff --git a/source/java/org/alfresco/service/cmr/rule/Rule.java b/source/java/org/alfresco/service/cmr/rule/Rule.java index 9ae30414a2..c0945facca 100644 --- a/source/java/org/alfresco/service/cmr/rule/Rule.java +++ b/source/java/org/alfresco/service/cmr/rule/Rule.java @@ -68,63 +68,117 @@ public class Rule implements Serializable */ private boolean executeAsynchronously = false; + /** Indicates wehther the rule is marked as disabled or not */ + private boolean ruleDisabled = false; + /** * Indicates whether the rule is applied to all the children of the associated node * rather than just the node itself. */ private boolean isAppliedToChildren = false; + /** + * Constructor + */ public Rule() { } + /** + * Constructor. + * + * @param nodeRef the rule node reference + */ public Rule(NodeRef nodeRef) { this.nodeRef = nodeRef; } + /** + * Set the action + * + * @param action the action + */ public void setAction(Action action) { this.action = action; } + /** + * Gets the action associatied with the rule + * + * @return the action + */ public Action getAction() { return action; } + /** + * Set the node reference of the rule + * + * @param nodeRef the rule node reference + */ public void setNodeRef(NodeRef nodeRef) { this.nodeRef = nodeRef; } + /** + * Get the node reference of the rule + * + * @return the rule node reference + */ public NodeRef getNodeRef() { return nodeRef; } + /** + * Set the title of the rule + * + * @param title the title + */ public void setTitle(String title) { this.title = title; } + /** + * Get the title of the rule + * + * @return the title + */ public String getTitle() { return title; } + /** + * Set the description of the rule + * + * @param description the description + */ public void setDescription(String description) { this.description = description; } + /** + * Get the description of the rule + * + * @return the description + */ public String getDescription() { return description; } /** - * @see org.alfresco.service.cmr.rule.Rule#isAppliedToChildren() + * Indicates wehther this rule should be applied to the children of + * the owning space. + * + * @return true if the rule is to be applied to children, false otherwise */ public boolean isAppliedToChildren() { @@ -132,13 +186,22 @@ public class Rule implements Serializable } /** - *@see org.alfresco.service.cmr.rule.Rule#applyToChildren(boolean) + * Sets the values that indicates whether this rule should be applied to the children + * of the owning space. + * + * @param isAppliedToChildren true if the rule is to be applied to children, false otherwise */ + public void applyToChildren(boolean isAppliedToChildren) { this.isAppliedToChildren = isAppliedToChildren; } + /** + * Helper method to set one rule type on the rule. + * + * @param ruleType the rule type + */ public void setRuleType(String ruleType) { List ruleTypes = new ArrayList(1); @@ -146,26 +209,67 @@ public class Rule implements Serializable this.ruleTypes = ruleTypes; } + /** + * Set the rules rule types. + * + * @param ruleTypes list of rule types + */ public void setRuleTypes(List ruleTypes) { this.ruleTypes = ruleTypes; } + /** + * Get the rules rule types. + * + * @return a list of rule types + */ public List getRuleTypes() { return ruleTypes; } + /** + * Sets the value that indicates whether this associated action should be executed + * asynchrously or not + * + * @param executeAsynchronously true to execute action async, false otherwise + */ public void setExecuteAsynchronously(boolean executeAsynchronously) { this.executeAsynchronously = executeAsynchronously; } + /** + * Indicates whether the associated action should be executed async or not + * + * @return true to execute async, false otherwise + */ public boolean getExecuteAsynchronously() { return this.executeAsynchronously; } + /** + * Indicates wehther this rule has been disabled or not + * + * @return true if the rule has been disabled, false otherwise + */ + public boolean getRuleDisabled() + { + return this.ruleDisabled; + } + + /** + * Set the value that indicates wehther this rule has been disabled or not + * + * @param ruleDisabled true id the rule has been disabled, false otherwise + */ + public void setRuleDisabled(boolean ruleDisabled) + { + this.ruleDisabled = ruleDisabled; + } + /** * Hash code implementation */ diff --git a/source/java/org/alfresco/service/cmr/rule/RuleType.java b/source/java/org/alfresco/service/cmr/rule/RuleType.java index 10fcb50d70..03c1fe8ab6 100644 --- a/source/java/org/alfresco/service/cmr/rule/RuleType.java +++ b/source/java/org/alfresco/service/cmr/rule/RuleType.java @@ -52,8 +52,9 @@ public interface RuleType /** * Trigger the rules of the rule type for the node on the actioned upon node. * - * @param nodeRef the node ref whos rule of rule type are to be triggered - * @param actionedUponNodeRef the node ref that the triggered rule will action upon + * @param nodeRef the node ref whos rule of rule type are to be triggered + * @param actionedUponNodeRef the node ref that the triggered rule will action upon + * @prarm executeRuleImmediately indicates whether the rule should be executed immediately or not */ - public void triggerRuleType(NodeRef nodeRef, NodeRef actionedUponNodeRef); + public void triggerRuleType(NodeRef nodeRef, NodeRef actionedUponNodeRef, boolean executeRuleImmediately); } \ No newline at end of file