Coverage Report - org.kuali.rice.kew.role.RoleRouteModule
 
Classes in this File Line Coverage Branch Coverage Complexity
RoleRouteModule
0%
0/115
0%
0/64
2.609
RoleRouteModule$ResponsibilitySet
0%
0/15
0%
0/10
2.609
 
 1  
 /*
 2  
  * Copyright 2007-2008 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kew.role;
 17  
 
 18  
 import java.util.ArrayList;
 19  
 import java.util.Collections;
 20  
 import java.util.List;
 21  
 
 22  
 import org.apache.commons.lang.StringUtils;
 23  
 import org.kuali.rice.core.exception.RiceRuntimeException;
 24  
 import org.kuali.rice.core.reflect.ObjectDefinition;
 25  
 import org.kuali.rice.core.resourceloader.GlobalResourceLoader;
 26  
 import org.kuali.rice.core.util.RiceDebugUtils;
 27  
 import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
 28  
 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
 29  
 import org.kuali.rice.kew.engine.RouteContext;
 30  
 import org.kuali.rice.kew.engine.node.RouteNodeUtils;
 31  
 import org.kuali.rice.kew.exception.WorkflowException;
 32  
 import org.kuali.rice.kew.routemodule.RouteModule;
 33  
 import org.kuali.rice.kew.rule.XmlConfiguredAttribute;
 34  
 import org.kuali.rice.kew.rule.bo.RuleAttribute;
 35  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 36  
 import org.kuali.rice.kew.util.KEWConstants;
 37  
 import org.kuali.rice.kew.util.ResponsibleParty;
 38  
 import org.kuali.rice.kim.bo.impl.KimAttributes;
 39  
 import org.kuali.rice.kim.bo.role.dto.ResponsibilityActionInfo;
 40  
 import org.kuali.rice.kim.bo.types.dto.AttributeSet;
 41  
 import org.kuali.rice.kim.service.KIMServiceLocator;
 42  
 import org.kuali.rice.kim.service.ResponsibilityService;
 43  
 
 44  
 /**
 45  
  * The RoleRouteModule is responsible for interfacing with the KIM
 46  
  * Role system to provide Role-based routing to KEW. 
 47  
  * 
 48  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 49  
  */
 50  0
 public class RoleRouteModule implements RouteModule {
 51  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RoleRouteModule.class);
 52  
         
 53  
         protected static final String QUALIFIER_RESOLVER_ELEMENT = KEWConstants.ROLEROUTE_QUALIFIER_RESOLVER_ELEMENT;
 54  
         protected static final String QUALIFIER_RESOLVER_CLASS_ELEMENT = KEWConstants.ROLEROUTE_QUALIFIER_RESOLVER_CLASS_ELEMENT;
 55  
         protected static final String RESPONSIBILITY_TEMPLATE_NAME_ELEMENT = KEWConstants.ROLEROUTE_RESPONSIBILITY_TEMPLATE_NAME_ELEMENT;
 56  
         protected static final String NAMESPACE_ELEMENT = KEWConstants.ROLEROUTE_NAMESPACE_ELEMENT;
 57  
         
 58  
         private static ResponsibilityService responsibilityService;
 59  
         
 60  
         private String qualifierResolverName;
 61  
         private String qualifierResolverClassName;
 62  
         private String responsibilityTemplateName;
 63  
         private String namespace;
 64  
                 
 65  
         @SuppressWarnings("unchecked")
 66  
         public List<ActionRequestValue> findActionRequests(RouteContext context)
 67  
                         throws Exception {
 68  
                 
 69  0
                 ActionRequestFactory arFactory = new ActionRequestFactory(context.getDocument(), context.getNodeInstance());
 70  0
                 List<ActionRequestValue> actionRequests = new ArrayList<ActionRequestValue>();
 71  0
                 QualifierResolver qualifierResolver = loadQualifierResolver(context);
 72  0
                 List<AttributeSet> qualifiers = qualifierResolver.resolve(context);
 73  0
                 String responsibilityTemplateName = loadResponsibilityTemplateName(context);
 74  0
                 String namespaceCode = loadNamespace(context);
 75  0
                 AttributeSet responsibilityDetails = loadResponsibilityDetails(context);
 76  0
                 if (LOG.isDebugEnabled()) {
 77  0
                         logQualifierCheck(namespaceCode, responsibilityTemplateName, responsibilityDetails, qualifiers);
 78  
                 }
 79  0
                 if ( qualifiers != null ) {
 80  0
                         for (AttributeSet qualifier : qualifiers) {
 81  0
                                 if ( qualifier.containsKey( KimAttributes.QUALIFIER_RESOLVER_PROVIDED_IDENTIFIER ) ) {
 82  0
                                         responsibilityDetails.put(KimAttributes.QUALIFIER_RESOLVER_PROVIDED_IDENTIFIER, qualifier.get(KimAttributes.QUALIFIER_RESOLVER_PROVIDED_IDENTIFIER));
 83  
                                 } else {
 84  0
                                         responsibilityDetails.remove( KimAttributes.QUALIFIER_RESOLVER_PROVIDED_IDENTIFIER );
 85  
                                 }
 86  0
                                 List<ResponsibilityActionInfo> responsibilities = getResponsibilityService().getResponsibilityActionsByTemplateName(namespaceCode, responsibilityTemplateName, qualifier, responsibilityDetails);
 87  0
                                 if (LOG.isDebugEnabled()) {
 88  0
                                         LOG.debug("Found " + responsibilities.size() + " responsibilities from ResponsibilityService");
 89  
                                 }
 90  
                                 // split the responsibility list defining characteristics (per the ResponsibilitySet.matches() method)
 91  0
                                 List<ResponsibilitySet> responsibilitySets = partitionResponsibilities(responsibilities);
 92  0
                                 if (LOG.isDebugEnabled()) {
 93  0
                                         LOG.debug("Found " + responsibilitySets.size() + " responsibility sets from ResponsibilityActionInfo list");
 94  
                                 }
 95  0
                                 for (ResponsibilitySet responsibilitySet : responsibilitySets) {
 96  0
                                         String approvePolicy = responsibilitySet.getApprovePolicy();
 97  
                                         // if all must approve, add the responsibilities individually so that the each get their own approval graph
 98  0
                                         if (KEWConstants.APPROVE_POLICY_ALL_APPROVE.equals(approvePolicy)) {
 99  0
                                                 for (ResponsibilityActionInfo responsibility : responsibilitySet.getResponsibilities()) {
 100  0
                                                         arFactory.addRoleResponsibilityRequest(Collections.singletonList(responsibility), approvePolicy);
 101  
                                                 }
 102  
                                         } else {
 103  
                                                 // first-approve policy, pass as groups to the ActionRequestFactory so that a single approval per set will clear the action request
 104  0
                                                 arFactory.addRoleResponsibilityRequest(responsibilitySet.getResponsibilities(), approvePolicy);
 105  
                                         }
 106  0
                                 }
 107  0
                         }                
 108  
                 }
 109  0
                 actionRequests = new ArrayList<ActionRequestValue>(arFactory.getRequestGraphs());
 110  0
                 disableResolveResponsibility(actionRequests);
 111  0
                 return actionRequests;
 112  
         }
 113  
         
 114  
     protected void logQualifierCheck(String namespaceCode, String responsibilityName, AttributeSet responsibilityDetails, List<AttributeSet> qualifiers ) {
 115  0
                 StringBuilder sb = new StringBuilder();
 116  0
                 sb.append(  '\n' );
 117  0
                 sb.append( "Get Resp Actions: " ).append( namespaceCode ).append( "/" ).append( responsibilityName ).append( '\n' );
 118  0
                 sb.append( "             Details:\n" );
 119  0
                 if ( responsibilityDetails != null ) {
 120  0
                         sb.append( responsibilityDetails.formattedDump( 25 ) );
 121  
                 } else {
 122  0
                         sb.append( "                         [null]\n" );
 123  
                 }
 124  0
                 sb.append( "             Qualifiers:\n" );
 125  0
                 for (AttributeSet qualification : qualifiers) {
 126  0
                         if ( qualification != null ) {
 127  0
                                 sb.append( qualification.formattedDump( 25 ) );
 128  
                         } else {
 129  0
                                 sb.append( "                         [null]\n" );
 130  
                         }
 131  
                 }
 132  0
                 if (LOG.isTraceEnabled()) { 
 133  0
                         LOG.trace( sb.append( RiceDebugUtils.getTruncatedStackTrace(true)).toString() ); 
 134  
                 } else {
 135  0
                         LOG.debug(sb.toString());
 136  
                 }
 137  0
     }
 138  
 
 139  
     /**
 140  
          * Walks the ActionRequest graph and disables responsibility resolution on those ActionRequests.
 141  
          * Because of the fact that it's not possible to tell if an ActionRequest was generated by
 142  
          * KIM once it's been saved in the database, we want to disable responsibilityId
 143  
          * resolution on the RouteModule because we will end up geting a reference to FlexRM and
 144  
          * a call to resolveResponsibilityId will fail.
 145  
          * 
 146  
          * @param actionRequests
 147  
          */
 148  
         protected void disableResolveResponsibility(List<ActionRequestValue> actionRequests) {
 149  0
                 for (ActionRequestValue actionRequest : actionRequests) {
 150  0
                         actionRequest.setResolveResponsibility(false);
 151  0
                         disableResolveResponsibility(actionRequest.getChildrenRequests());
 152  
                 }
 153  0
         }
 154  
 
 155  
         protected QualifierResolver loadQualifierResolver(RouteContext context) {
 156  0
                 if (StringUtils.isBlank(qualifierResolverName)) {
 157  0
                         this.qualifierResolverName = RouteNodeUtils.getValueOfCustomProperty(context.getNodeInstance().getRouteNode(), QUALIFIER_RESOLVER_ELEMENT);
 158  
                 }
 159  0
                 if (StringUtils.isBlank(qualifierResolverClassName)) {                        
 160  0
                         this.qualifierResolverClassName = RouteNodeUtils.getValueOfCustomProperty(context.getNodeInstance().getRouteNode(), QUALIFIER_RESOLVER_CLASS_ELEMENT);
 161  
                 }
 162  0
                 QualifierResolver resolver = null;
 163  0
                 if (!StringUtils.isBlank(qualifierResolverName)) {
 164  0
                         RuleAttribute ruleAttribute = KEWServiceLocator.getRuleAttributeService().findByName(qualifierResolverName);
 165  0
                         if (ruleAttribute == null) {
 166  0
                                 throw new RiceRuntimeException("Failed to locate QualifierResolver for attribute name: " + qualifierResolverName);
 167  
                         }
 168  0
                         ObjectDefinition definition = getAttributeObjectDefinition(ruleAttribute);
 169  0
                         resolver = (QualifierResolver)GlobalResourceLoader.getObject(definition);
 170  0
                         if (resolver instanceof XmlConfiguredAttribute) {
 171  0
                                 ((XmlConfiguredAttribute)resolver).setRuleAttribute(ruleAttribute);
 172  
                         }
 173  
                 }
 174  0
                 if (resolver == null && !StringUtils.isBlank(qualifierResolverClassName)) {
 175  0
                         resolver = (QualifierResolver)GlobalResourceLoader.getObject(new ObjectDefinition(qualifierResolverClassName));
 176  
                 }
 177  0
                 if (resolver == null) {
 178  0
                         resolver = new NullQualifierResolver();
 179  
                 }
 180  0
                 if (LOG.isDebugEnabled()) {
 181  0
                         LOG.debug("Resolver class being returned: " + resolver.getClass().getName());
 182  
                 }
 183  0
                 return resolver;
 184  
         }
 185  
         
 186  
         protected AttributeSet loadResponsibilityDetails(RouteContext context) {
 187  0
                 String documentTypeName = context.getDocument().getDocumentType().getName();
 188  0
                 String nodeName = context.getNodeInstance().getName();
 189  0
                 AttributeSet responsibilityDetails = new AttributeSet();
 190  0
                 responsibilityDetails.put(KEWConstants.DOCUMENT_TYPE_NAME_DETAIL, documentTypeName);
 191  0
                 responsibilityDetails.put(KEWConstants.ROUTE_NODE_NAME_DETAIL, nodeName);
 192  0
                 return responsibilityDetails;
 193  
         }
 194  
         
 195  
         protected String loadResponsibilityTemplateName(RouteContext context) {
 196  0
                 if (StringUtils.isBlank(responsibilityTemplateName)) {
 197  0
                         this.responsibilityTemplateName = RouteNodeUtils.getValueOfCustomProperty(context.getNodeInstance().getRouteNode(), RESPONSIBILITY_TEMPLATE_NAME_ELEMENT);
 198  
                 }
 199  0
                 if (StringUtils.isBlank(responsibilityTemplateName)) {
 200  0
                         this.responsibilityTemplateName = KEWConstants.DEFAULT_RESPONSIBILITY_TEMPLATE_NAME;
 201  
                 }
 202  0
                 return responsibilityTemplateName;
 203  
         }
 204  
         
 205  
         protected String loadNamespace(RouteContext context) {
 206  0
                 if (StringUtils.isBlank(namespace)) {
 207  0
                         this.namespace = RouteNodeUtils.getValueOfCustomProperty(context.getNodeInstance().getRouteNode(), NAMESPACE_ELEMENT);
 208  
                 }
 209  0
                 if (StringUtils.isBlank(namespace)) {
 210  0
                         this.namespace = KEWConstants.KEW_NAMESPACE;
 211  
                 }
 212  0
                 return namespace;
 213  
         }
 214  
         
 215  
     protected ObjectDefinition getAttributeObjectDefinition(RuleAttribute ruleAttribute) {
 216  0
             return new ObjectDefinition(ruleAttribute.getClassName(), ruleAttribute.getServiceNamespace());
 217  
     }
 218  
     
 219  
     protected List<ResponsibilitySet> partitionResponsibilities(List<ResponsibilityActionInfo> responsibilities) {
 220  0
             List<ResponsibilitySet> responsibilitySets = new ArrayList<ResponsibilitySet>();
 221  0
             for (ResponsibilityActionInfo responsibility : responsibilities) {
 222  0
                     ResponsibilitySet targetResponsibilitySet = null;
 223  0
                     for (ResponsibilitySet responsibiliySet : responsibilitySets) {
 224  0
                             if (responsibiliySet.matches(responsibility)) {
 225  0
                                     targetResponsibilitySet = responsibiliySet;
 226  
                             }
 227  
                     }
 228  0
                     if (targetResponsibilitySet == null) {
 229  0
                             targetResponsibilitySet = new ResponsibilitySet(responsibility);
 230  0
                             responsibilitySets.add(targetResponsibilitySet);
 231  
                     }
 232  0
                     targetResponsibilitySet.getResponsibilities().add(responsibility);
 233  0
             }
 234  0
             return responsibilitySets;
 235  
     }
 236  
         
 237  
         /**
 238  
          * Return null so that the responsibility ID will remain the same.
 239  
          *
 240  
          * @see org.kuali.rice.kew.routemodule.RouteModule#resolveResponsibilityId(java.lang.Long)
 241  
          */
 242  
         public ResponsibleParty resolveResponsibilityId(Long responsibilityId)
 243  
                         throws WorkflowException {
 244  0
                 return null;
 245  
         }
 246  
         
 247  
         
 248  
         
 249  0
         private static class ResponsibilitySet {
 250  
                 private String actionRequestCode;
 251  
                 private String approvePolicy;
 252  
                 private Integer priorityNumber;
 253  
                 private String parallelRoutingGroupingCode;
 254  
                 private String roleResponsibilityActionId;
 255  0
                 private List<ResponsibilityActionInfo> responsibilities = new ArrayList<ResponsibilityActionInfo>();
 256  
 
 257  0
                 public ResponsibilitySet(ResponsibilityActionInfo responsibility) {
 258  0
                         this.actionRequestCode = responsibility.getActionTypeCode();
 259  0
                         this.approvePolicy = responsibility.getActionPolicyCode();
 260  0
                         this.priorityNumber = responsibility.getPriorityNumber();
 261  0
                         this.parallelRoutingGroupingCode = responsibility.getParallelRoutingGroupingCode();
 262  0
                         this.roleResponsibilityActionId = responsibility.getRoleResponsibilityActionId();
 263  0
                 }
 264  
                 
 265  
                 public boolean matches(ResponsibilityActionInfo responsibility) {
 266  0
                         return responsibility.getActionTypeCode().equals(actionRequestCode) &&
 267  
                                 responsibility.getActionPolicyCode().equals(approvePolicy) && 
 268  
                                 responsibility.getPriorityNumber().equals( priorityNumber ) &&
 269  
                                 responsibility.getParallelRoutingGroupingCode().equals( parallelRoutingGroupingCode ) &&
 270  
                                 responsibility.getRoleResponsibilityActionId().equals( roleResponsibilityActionId );
 271  
                 }
 272  
 
 273  
                 public String getActionRequestCode() {
 274  0
                         return this.actionRequestCode;
 275  
                 }
 276  
 
 277  
                 public String getApprovePolicy() {
 278  0
                         return this.approvePolicy;
 279  
                 }
 280  
                 
 281  
                 public Integer getPriorityNumber() {
 282  0
                         return priorityNumber;
 283  
                 }
 284  
 
 285  
                 public List<ResponsibilityActionInfo> getResponsibilities() {
 286  0
                         return this.responsibilities;
 287  
                 }
 288  
 
 289  
                 public String getParallelRoutingGroupingCode() {
 290  0
                         return this.parallelRoutingGroupingCode;
 291  
                 }
 292  
 
 293  
                 public String getRoleResponsibilityActionId() {
 294  0
                         return this.roleResponsibilityActionId;
 295  
                 }                
 296  
                 
 297  
         }
 298  
 
 299  
 
 300  
 
 301  
         /**
 302  
          * @param qualifierResolverName the qualifierResolverName to set
 303  
          */
 304  
         public void setQualifierResolverName(String qualifierResolverName) {
 305  0
                 this.qualifierResolverName = qualifierResolverName;
 306  0
         }
 307  
 
 308  
         /**
 309  
          * @param qualifierResolverClassName the qualifierResolverClassName to set
 310  
          */
 311  
         public void setQualifierResolverClassName(String qualifierResolverClassName) {
 312  0
                 this.qualifierResolverClassName = qualifierResolverClassName;
 313  0
         }
 314  
 
 315  
         /**
 316  
          * @param responsibilityTemplateName the responsibilityTemplateName to set
 317  
          */
 318  
         public void setResponsibilityTemplateName(String responsibilityTemplateName) {
 319  0
                 this.responsibilityTemplateName = responsibilityTemplateName;
 320  0
         }
 321  
 
 322  
         /**
 323  
          * @param namespace the namespace to set
 324  
          */
 325  
         public void setNamespace(String namespace) {
 326  0
                 this.namespace = namespace;
 327  0
         }
 328  
 
 329  
         protected ResponsibilityService getResponsibilityService() {
 330  0
                 if ( responsibilityService == null ) {
 331  0
                         responsibilityService = KIMServiceLocator.getResponsibilityService();
 332  
                 }
 333  0
                 return responsibilityService;
 334  
         }
 335  
 
 336  
 }