1 17 package org.alfresco.repo.rule; 18 19 import java.io.Serializable ; 20 import java.util.ArrayList ; 21 import java.util.Date ; 22 import java.util.HashMap ; 23 import java.util.HashSet ; 24 import java.util.List ; 25 import java.util.Map ; 26 import java.util.Set ; 27 28 import org.alfresco.model.ContentModel; 29 import org.alfresco.repo.action.ActionModel; 30 import org.alfresco.repo.action.RuntimeActionService; 31 import org.alfresco.repo.transaction.AlfrescoTransactionSupport; 32 import org.alfresco.repo.transaction.TransactionListener; 33 import org.alfresco.service.cmr.action.ActionService; 34 import org.alfresco.service.cmr.action.ActionServiceException; 35 import org.alfresco.service.cmr.dictionary.DictionaryService; 36 import org.alfresco.service.cmr.repository.ChildAssociationRef; 37 import org.alfresco.service.cmr.repository.NodeRef; 38 import org.alfresco.service.cmr.repository.NodeService; 39 import org.alfresco.service.cmr.rule.Rule; 40 import org.alfresco.service.cmr.rule.RuleService; 41 import org.alfresco.service.cmr.rule.RuleServiceException; 42 import org.alfresco.service.cmr.rule.RuleType; 43 import org.alfresco.service.cmr.search.SearchService; 44 import org.alfresco.service.namespace.DynamicNamespacePrefixResolver; 45 import org.alfresco.service.namespace.NamespaceService; 46 import org.alfresco.service.namespace.QName; 47 import org.alfresco.service.namespace.RegexQNamePattern; 48 import org.alfresco.util.GUID; 49 import org.apache.commons.logging.Log; 50 import org.apache.commons.logging.LogFactory; 51 52 61 public class RuleServiceImpl implements RuleService, RuntimeRuleService 62 { 63 64 private static final String KEY_RULES_PENDING = "RuleServiceImpl.PendingRules"; 65 66 67 private static final String KEY_RULES_EXECUTED = "RuleServiceImpl.ExecutedRules"; 68 69 70 private QName ASSOC_NAME_RULES = QName.createQName(RuleModel.RULE_MODEL_URI, "rules"); 71 72 75 private static Log logger = LogFactory.getLog(RuleServiceImpl.class); 76 77 80 private NodeService nodeService; 81 82 85 private NodeService runtimeNodeService; 86 87 90 private ActionService actionService; 91 92 95 private SearchService searchService; 96 97 100 private DictionaryService dictionaryService; 101 102 105 RuntimeActionService runtimeActionService; 106 107 111 private Set <NodeRef> disabledNodeRefs = new HashSet <NodeRef>(5); 112 113 117 private Set <Rule> disabledRules = new HashSet <Rule>(5); 118 119 122 private Map <String , RuleType> ruleTypes = new HashMap <String , RuleType>(); 123 124 127 private TransactionListener ruleTransactionListener = new RuleTransactionListener(this); 128 129 134 public void setNodeService(NodeService nodeService) 135 { 136 this.nodeService = nodeService; 137 } 138 139 144 public void setRuntimeNodeService(NodeService runtimeNodeService) 145 { 146 this.runtimeNodeService = runtimeNodeService; 147 } 148 149 154 public void setActionService(ActionService actionService) 155 { 156 this.actionService = actionService; 157 } 158 159 164 public void setRuntimeActionService(RuntimeActionService runtimeActionService) 165 { 166 this.runtimeActionService = runtimeActionService; 167 } 168 169 174 public void setSearchService(SearchService searchService) 175 { 176 this.searchService = searchService; 177 } 178 179 184 public void setDictionaryService(DictionaryService dictionaryService) 185 { 186 this.dictionaryService = dictionaryService; 187 } 188 189 195 private NodeRef getSavedRuleFolderRef(NodeRef nodeRef) 196 { 197 NodeRef result = null; 198 199 List <ChildAssociationRef> assocs = this.runtimeNodeService.getChildAssocs( 200 nodeRef, 201 RegexQNamePattern.MATCH_ALL, 202 RuleModel.ASSOC_RULE_FOLDER); 203 if (assocs.size() > 1) 204 { 205 throw new ActionServiceException("There is more than one rule folder, which is invalid."); 206 } 207 else if (assocs.size() == 1) 208 { 209 result = assocs.get(0).getChildRef(); 210 } 211 212 return result; 213 } 214 215 218 public List <RuleType> getRuleTypes() 219 { 220 return new ArrayList <RuleType>(this.ruleTypes.values()); 221 } 222 223 226 public RuleType getRuleType(String name) 227 { 228 return this.ruleTypes.get(name); 229 } 230 231 234 public boolean rulesEnabled(NodeRef nodeRef) 235 { 236 return (this.disabledNodeRefs.contains(nodeRef) == false); 237 } 238 239 242 public void disableRules(NodeRef nodeRef) 243 { 244 this.disabledNodeRefs.add(nodeRef); 246 } 247 248 251 public void enableRules(NodeRef nodeRef) 252 { 253 this.disabledNodeRefs.remove(nodeRef); 255 } 256 257 260 public void disableRule(Rule rule) 261 { 262 this.disabledRules.add(rule); 263 } 264 265 268 public void enableRule(Rule rule) 269 { 270 this.disabledRules.remove(rule); 271 } 272 273 276 public boolean hasRules(NodeRef nodeRef) 277 { 278 return getRules(nodeRef).size() != 0; 279 } 280 281 284 public List <Rule> getRules(NodeRef nodeRef) 285 { 286 return getRules(nodeRef, true, null); 287 } 288 289 292 public List <Rule> getRules(NodeRef nodeRef, boolean includeInherited) 293 { 294 return getRules(nodeRef, includeInherited, null); 295 } 296 297 300 public List <Rule> getRules(NodeRef nodeRef, boolean includeInherited, String ruleTypeName) 301 { 302 List <Rule> rules = new ArrayList <Rule>(); 303 304 if (this.runtimeNodeService.exists(nodeRef) == true && checkNodeType(nodeRef) == true) 305 { 306 if (includeInherited == true) 307 { 308 for (Rule rule : getInheritedRules(nodeRef, ruleTypeName, null)) 310 { 311 if (rules.contains(rule) == false) 313 { 314 rules.add(rule); 315 } 316 } 317 } 318 319 if (this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) 320 { 321 NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef); 322 if (ruleFolder != null) 323 { 324 List <Rule> allRules = new ArrayList <Rule>(); 325 326 List <ChildAssociationRef> ruleChildAssocRefs = 328 this.runtimeNodeService.getChildAssocs(ruleFolder, RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES); 329 for (ChildAssociationRef ruleChildAssocRef : ruleChildAssocRefs) 330 { 331 NodeRef ruleNodeRef = ruleChildAssocRef.getChildRef(); 333 Rule rule = createRule(nodeRef, ruleNodeRef); 334 allRules.add(rule); 335 } 336 337 for (Rule rule : allRules) 339 { 340 if ((rules.contains(rule) == false) && 341 (ruleTypeName == null || ruleTypeName.equals(rule.getRuleTypeName()) == true)) 342 { 343 rules.add(rule); 344 } 345 } 346 } 347 } 348 } 349 350 return rules; 351 } 352 353 356 public int countRules(NodeRef nodeRef) 357 { 358 int ruleCount = 0; 359 360 if (this.runtimeNodeService.exists(nodeRef) == true && checkNodeType(nodeRef) == true) 361 { 362 if (this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) 363 { 364 NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef); 365 if (ruleFolder != null) 366 { 367 List <ChildAssociationRef> ruleChildAssocRefs = 369 this.runtimeNodeService.getChildAssocs(ruleFolder, RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES); 370 371 ruleCount = ruleChildAssocRefs.size(); 372 } 373 } 374 } 375 376 return ruleCount; 377 } 378 379 385 private boolean checkNodeType(NodeRef nodeRef) 386 { 387 boolean result = true; 388 389 QName nodeType = this.runtimeNodeService.getType(nodeRef); 390 if (this.dictionaryService.isSubClass(nodeType, ContentModel.TYPE_SYSTEM_FOLDER) == true || 391 this.dictionaryService.isSubClass(nodeType, ActionModel.TYPE_ACTION) == true || 392 this.dictionaryService.isSubClass(nodeType, ActionModel.TYPE_ACTION_CONDITION) == true || 393 this.dictionaryService.isSubClass(nodeType, ActionModel.TYPE_ACTION_PARAMETER) == true) 394 { 395 result = false; 396 397 if (logger.isDebugEnabled() == true) 398 { 399 logger.debug("A node of type " + nodeType.toString() + " was checked and can not have rules."); 400 } 401 } 402 403 return result; 404 } 405 406 413 private List <Rule> getInheritedRules(NodeRef nodeRef, String ruleTypeName, Set <NodeRef> visitedNodeRefs) 414 { 415 List <Rule> inheritedRules = new ArrayList <Rule>(); 416 417 if (visitedNodeRefs == null) 419 { 420 visitedNodeRefs = new HashSet <NodeRef>(); 421 } 422 423 if (visitedNodeRefs.contains(nodeRef) == false) 425 { 426 visitedNodeRefs.add(nodeRef); 427 428 List <Rule> allInheritedRules = new ArrayList <Rule>(); 429 List <ChildAssociationRef> parents = this.runtimeNodeService.getParentAssocs(nodeRef); 430 for (ChildAssociationRef parent : parents) 431 { 432 List <Rule> rules = getRules(parent.getParentRef(), false); 433 for (Rule rule : rules) 434 { 435 if (rule.isAppliedToChildren() == true && allInheritedRules.contains(rule) == false) 437 { 438 allInheritedRules.add(rule); 439 } 440 } 441 442 for (Rule rule : getInheritedRules(parent.getParentRef(), ruleTypeName, visitedNodeRefs)) 443 { 444 if (allInheritedRules.contains(rule) == false) 446 { 447 allInheritedRules.add(rule); 448 } 449 } 450 } 451 452 if (ruleTypeName == null) 453 { 454 inheritedRules = allInheritedRules; 455 } 456 else 457 { 458 for (Rule rule : allInheritedRules) 460 { 461 if (rule.getRuleTypeName().equals(ruleTypeName) == true) 462 { 463 inheritedRules.add(rule); 464 } 465 } 466 } 467 } 468 469 return inheritedRules; 470 } 471 472 475 public Rule getRule(NodeRef nodeRef, String ruleId) 476 { 477 Rule rule = null; 478 479 if (this.runtimeNodeService.exists(nodeRef) == true) 480 { 481 NodeRef ruleNodeRef = getRuleNodeRefFromId(nodeRef, ruleId); 482 if (ruleNodeRef != null) 483 { 484 rule = createRule(nodeRef, ruleNodeRef); 485 } 486 } 487 488 return rule; 489 } 490 491 498 private NodeRef getRuleNodeRefFromId(NodeRef nodeRef, String ruleId) 499 { 500 NodeRef result = null; 501 if (this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) 502 { 503 NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef); 504 if (ruleFolder != null) 505 { 506 DynamicNamespacePrefixResolver namespacePrefixResolver = new DynamicNamespacePrefixResolver(); 507 namespacePrefixResolver.registerNamespace(NamespaceService.SYSTEM_MODEL_PREFIX, NamespaceService.SYSTEM_MODEL_1_0_URI); 508 509 List <NodeRef> nodeRefs = searchService.selectNodes( 510 ruleFolder, 511 "*[@sys:" + ContentModel.PROP_NODE_UUID.getLocalName() + "='" + ruleId + "']", 512 null, 513 namespacePrefixResolver, 514 false); 515 if (nodeRefs.size() != 0) 516 { 517 result = nodeRefs.get(0); 518 } 519 } 520 } 521 522 return result; 523 } 524 525 531 private Rule createRule(NodeRef owningNodeRef, NodeRef ruleNodeRef) 532 { 533 Map <QName, Serializable > props = this.nodeService.getProperties(ruleNodeRef); 535 536 String ruleTypeName = (String )props.get(RuleModel.PROP_RULE_TYPE); 538 Rule rule = new RuleImpl(ruleNodeRef.getId(), ruleTypeName, owningNodeRef); 539 540 boolean isAppliedToChildren = false; 542 Boolean value = (Boolean )props.get(RuleModel.PROP_APPLY_TO_CHILDREN); 543 if (value != null) 544 { 545 isAppliedToChildren = value.booleanValue(); 546 } 547 rule.applyToChildren(isAppliedToChildren); 548 549 runtimeActionService.populateCompositeAction(ruleNodeRef, rule); 551 552 return rule; 553 } 554 555 558 public Rule createRule(String ruleTypeName) 559 { 560 String id = GUID.generate(); 562 return new RuleImpl(id, ruleTypeName, null); 563 } 564 565 568 public void saveRule(NodeRef nodeRef, Rule rule) 569 { 570 if (this.nodeService.exists(nodeRef) == false) 571 { 572 throw new RuleServiceException("The node does not exist."); 573 } 574 575 NodeRef ruleNodeRef = getRuleNodeRefFromId(nodeRef, rule.getId()); 576 if (ruleNodeRef == null) 577 { 578 if (this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == false) 579 { 580 this.nodeService.addAspect(nodeRef, RuleModel.ASPECT_RULES, null); 582 } 583 584 Map <QName, Serializable > props = new HashMap <QName, Serializable >(3); 585 props.put(RuleModel.PROP_RULE_TYPE, rule.getRuleTypeName()); 586 props.put(ActionModel.PROP_DEFINITION_NAME, rule.getActionDefinitionName()); 587 props.put(ContentModel.PROP_NODE_UUID, rule.getId()); 588 589 ruleNodeRef = this.nodeService.createNode( 591 getSavedRuleFolderRef(nodeRef), 592 ContentModel.ASSOC_CONTAINS, 593 ASSOC_NAME_RULES, 594 RuleModel.TYPE_RULE, 595 props).getChildRef(); 596 597 ((RuleImpl)rule).setCreator((String )this.nodeService.getProperty(ruleNodeRef, ContentModel.PROP_CREATOR)); 599 ((RuleImpl)rule).setCreatedDate((Date )this.nodeService.getProperty(ruleNodeRef, ContentModel.PROP_CREATED)); 600 } 601 602 this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_APPLY_TO_CHILDREN, rule.isAppliedToChildren()); 604 605 runtimeActionService.saveActionImpl(nodeRef, ruleNodeRef, rule); 607 } 608 609 612 public void removeRule(NodeRef nodeRef, Rule rule) 613 { 614 if (this.nodeService.exists(nodeRef) == true && 615 this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) 616 { 617 disableRules(nodeRef); 618 try 619 { 620 NodeRef ruleNodeRef = getRuleNodeRefFromId(nodeRef, rule.getId()); 621 if (ruleNodeRef != null) 622 { 623 this.nodeService.removeChild(getSavedRuleFolderRef(nodeRef), ruleNodeRef); 624 } 625 } 626 finally 627 { 628 enableRules(nodeRef); 629 } 630 } 631 } 632 633 636 public void removeAllRules(NodeRef nodeRef) 637 { 638 if (this.nodeService.exists(nodeRef) == true && 639 this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) 640 { 641 NodeRef folder = getSavedRuleFolderRef(nodeRef); 642 if (folder != null) 643 { 644 List <ChildAssociationRef> ruleChildAssocs = this.nodeService.getChildAssocs( 645 folder, 646 RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES); 647 for (ChildAssociationRef ruleChildAssoc : ruleChildAssocs) 648 { 649 this.nodeService.removeChild(folder, ruleChildAssoc.getChildRef()); 650 } 651 } 652 } 653 } 654 655 @SuppressWarnings ("unchecked") 656 public void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule) 657 { 658 addRulePendingExecution(actionableNodeRef, actionedUponNodeRef, rule, false); 659 } 660 661 @SuppressWarnings ("unchecked") 662 public void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule, boolean executeAtEnd) 663 { 664 if (this.disabledNodeRefs.contains(rule.getOwningNodeRef()) == false && 666 this.disabledRules.contains(rule) == false) 667 { 668 PendingRuleData pendingRuleData = new PendingRuleData(actionableNodeRef, actionedUponNodeRef, rule, executeAtEnd); 669 Set <ExecutedRuleData> executedRules = 670 (Set <ExecutedRuleData>) AlfrescoTransactionSupport.getResource(KEY_RULES_EXECUTED); 671 672 if (executedRules == null || executedRules.contains(new ExecutedRuleData(actionableNodeRef, rule)) == false) 673 { 674 Set <PendingRuleData> pendingRules = 675 (Set <PendingRuleData>) AlfrescoTransactionSupport.getResource(KEY_RULES_PENDING); 676 if (pendingRules == null) 677 { 678 pendingRules = new HashSet <PendingRuleData>(); 680 AlfrescoTransactionSupport.bindResource(KEY_RULES_PENDING, pendingRules); 681 AlfrescoTransactionSupport.bindListener(this.ruleTransactionListener); 683 684 if (logger.isDebugEnabled() == true) 685 { 686 logger.debug("Rule '" + rule.getTitle() + "' has been added pending execution to action upon node '" + actionedUponNodeRef.getId() + "'"); 687 } 688 } 689 690 pendingRules.add(pendingRuleData); 692 } 693 } 694 else 695 { 696 if (logger.isDebugEnabled() == true) 697 { 698 logger.debug("The rule '" + rule.getTitle() + "' or the node '" + rule.getOwningNodeRef().getId() + "' has been disabled."); 699 } 700 } 701 } 702 703 706 public void executePendingRules() 707 { 708 AlfrescoTransactionSupport.bindResource(KEY_RULES_EXECUTED, new HashSet <ExecutedRuleData>()); 709 try 710 { 711 List <PendingRuleData> executeAtEndRules = new ArrayList <PendingRuleData>(); 712 executePendingRulesImpl(executeAtEndRules); 713 for (PendingRuleData data : executeAtEndRules) 714 { 715 executePendingRule(data); 716 } 717 } 718 finally 719 { 720 AlfrescoTransactionSupport.unbindResource(KEY_RULES_EXECUTED); 721 } 722 } 723 724 727 @SuppressWarnings ("unchecked") 728 private void executePendingRulesImpl(List <PendingRuleData> executeAtEndRules) 729 { 730 Set <PendingRuleData> pendingRules = 732 (Set <PendingRuleData>) AlfrescoTransactionSupport.getResource(KEY_RULES_PENDING); 733 if (pendingRules != null && !pendingRules.isEmpty()) 735 { 736 PendingRuleData[] pendingRulesArr = pendingRules.toArray(new PendingRuleData[0]); 737 AlfrescoTransactionSupport.unbindResource(KEY_RULES_PENDING); 739 for (PendingRuleData pendingRule : pendingRulesArr) 741 { 742 if (pendingRule.getExecuteAtEnd() == false) 743 { 744 executePendingRule(pendingRule); 745 } 746 else 747 { 748 executeAtEndRules.add(pendingRule); 749 } 750 } 751 752 executePendingRulesImpl(executeAtEndRules); 754 } 755 } 756 757 762 @SuppressWarnings ("unchecked") 763 private void executePendingRule(PendingRuleData pendingRule) 764 { 765 NodeRef actionableNodeRef = pendingRule.getActionableNodeRef(); 766 NodeRef actionedUponNodeRef = pendingRule.getActionedUponNodeRef(); 767 Rule rule = pendingRule.getRule(); 768 769 if (this.actionService.evaluateAction(rule, actionedUponNodeRef) == true) 771 { 772 Set <ExecutedRuleData> executedRules = 775 (Set <ExecutedRuleData>) AlfrescoTransactionSupport.getResource(KEY_RULES_EXECUTED); 776 executedRules.add(new ExecutedRuleData(actionableNodeRef, rule)); 777 778 this.actionService.executeAction(rule, actionedUponNodeRef); 780 } 781 } 782 783 788 public void registerRuleType(RuleType ruleType) 789 { 790 this.ruleTypes.put(ruleType.getName(), ruleType); 791 } 792 793 798 private class ExecutedRuleData 799 { 800 801 protected NodeRef actionableNodeRef; 802 protected Rule rule; 803 804 public ExecutedRuleData(NodeRef actionableNodeRef, Rule rule) 805 { 806 this.actionableNodeRef = actionableNodeRef; 807 this.rule = rule; 808 } 809 810 public NodeRef getActionableNodeRef() 811 { 812 return actionableNodeRef; 813 } 814 815 public Rule getRule() 816 { 817 return rule; 818 } 819 820 @Override 821 public int hashCode() 822 { 823 int i = actionableNodeRef.hashCode(); 824 i = (i*37) + rule.hashCode(); 825 return i; 826 } 827 828 @Override 829 public boolean equals(Object obj) 830 { 831 if (this == obj) 832 { 833 return true; 834 } 835 if (obj instanceof ExecutedRuleData) 836 { 837 ExecutedRuleData that = (ExecutedRuleData) obj; 838 return (this.actionableNodeRef.equals(that.actionableNodeRef) && 839 this.rule.equals(that.rule)); 840 } 841 else 842 { 843 return false; 844 } 845 } 846 } 847 848 853 private class PendingRuleData extends ExecutedRuleData 854 { 855 private NodeRef actionedUponNodeRef; 856 private boolean executeAtEnd = false; 857 858 public PendingRuleData(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule, boolean executeAtEnd) 859 { 860 super(actionableNodeRef, rule); 861 this.actionedUponNodeRef = actionedUponNodeRef; 862 this.executeAtEnd = executeAtEnd; 863 } 864 865 public NodeRef getActionedUponNodeRef() 866 { 867 return actionedUponNodeRef; 868 } 869 870 public boolean getExecuteAtEnd() 871 { 872 return this.executeAtEnd; 873 } 874 875 @Override 876 public int hashCode() 877 { 878 int i = super.hashCode(); 879 i = (i*37) + actionedUponNodeRef.hashCode(); 880 return i; 881 } 882 883 @Override 884 public boolean equals(Object obj) 885 { 886 if (this == obj) 887 { 888 return true; 889 } 890 if (obj instanceof PendingRuleData) 891 { 892 PendingRuleData that = (PendingRuleData) obj; 893 return (this.actionableNodeRef.equals(that.actionableNodeRef) && 894 this.actionedUponNodeRef.equals(that.actionedUponNodeRef) && 895 this.rule.equals(that.rule)); 896 } 897 else 898 { 899 return false; 900 } 901 } 902 } 903 } 904 | Popular Tags |