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