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