View Javadoc
1   /**
2    * Copyright 2005-2015 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.impl.peopleflow;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.core.api.CoreConstants;
21  import org.kuali.rice.core.api.config.ConfigurationException;
22  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
23  import org.kuali.rice.core.api.exception.RiceIllegalStateException;
24  import org.kuali.rice.core.api.exception.RiceRuntimeException;
25  import org.kuali.rice.core.api.membership.MemberType;
26  import org.kuali.rice.core.api.util.VersionHelper;
27  import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
28  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
29  import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
30  import org.kuali.rice.kew.actionrequest.KimPrincipalRecipient;
31  import org.kuali.rice.kew.actionrequest.Recipient;
32  import org.kuali.rice.kew.api.action.ActionRequestPolicy;
33  import org.kuali.rice.kew.api.action.ActionRequestType;
34  import org.kuali.rice.kew.api.action.RecipientType;
35  import org.kuali.rice.kew.api.document.Document;
36  import org.kuali.rice.kew.api.document.DocumentContent;
37  import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
38  import org.kuali.rice.kew.api.peopleflow.PeopleFlowDelegate;
39  import org.kuali.rice.kew.api.peopleflow.PeopleFlowMember;
40  import org.kuali.rice.kew.api.repository.type.KewTypeDefinition;
41  import org.kuali.rice.kew.api.repository.type.KewTypeRepositoryService;
42  import org.kuali.rice.kew.engine.RouteContext;
43  import org.kuali.rice.kew.framework.peopleflow.PeopleFlowTypeService;
44  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
45  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValueContent;
46  import org.kuali.rice.kim.api.group.Group;
47  import org.kuali.rice.kim.api.identity.principal.Principal;
48  import org.kuali.rice.kim.api.role.Role;
49  import org.kuali.rice.kim.api.role.RoleMembership;
50  import org.kuali.rice.kim.api.role.RoleService;
51  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
52  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
53  import org.kuali.rice.ksb.api.bus.Endpoint;
54  
55  import javax.xml.namespace.QName;
56  import java.util.ArrayList;
57  import java.util.Collections;
58  import java.util.List;
59  import java.util.Map;
60  
61  /**
62   * Reference implementation of the {@code PeopleFlowRequestGenerator} which is responsible for generating Action
63   * Requests from a {@link PeopleFlowDefinition}.
64   *
65   * @author Kuali Rice Team (rice.collab@kuali.org)
66   */
67  public class PeopleFlowRequestGeneratorImpl implements PeopleFlowRequestGenerator {
68  
69      private KewTypeRepositoryService typeRepositoryService;
70      private RoleService roleService;
71  
72      @Override
73      public List<ActionRequestValue> generateRequests(RouteContext routeContext, PeopleFlowDefinition peopleFlow, ActionRequestType actionRequested) {
74          Context context = new Context(routeContext, peopleFlow, actionRequested);
75          for (PeopleFlowMember member : peopleFlow.getMembers()) {
76              generateRequestForMember(context, member);
77          }
78  
79          return context.getActionRequestFactory().getRequestGraphs();
80      }
81  
82      protected void generateRequestForMember(Context context, PeopleFlowMember member) {
83          if (MemberType.ROLE == member.getMemberType()) {
84              // for role members, delegates are generated within this call
85              generateRequestsForRoleMember(context, member);
86          } else {
87              ActionRequestValue actionRequest = context.getActionRequestFactory().addRootActionRequest(
88                      context.getActionRequested().getCode(), member.getPriority(), toRecipient(member), "",
89                      member.getResponsibilityId(), member.getForceAction(), getActionRequestPolicyCode(member), null);
90  
91              if (actionRequest != null) {
92                  generateDelegationRequests(context, Collections.singletonList(actionRequest), member);
93              }
94          }
95      }
96  
97      /**
98       * generates requests for a PeopleFlow member of type Role.
99       *
100      * <p>Will resolve role qualifiers through the appropriate PeopleFlowTypeService, and if multiple qualifier maps are
101      * generated, it can generate multiple request trees.
102      * parent requests</p>
103      *
104      * @param context the context for request generation
105      * @param member the PeopleFlow member, which is assumed to be of MemberType ROLE
106      * @return a list of root action requests that were generated, or an empty list if no role members were resolved
107      */
108     protected List<ActionRequestValue> generateRequestsForRoleMember(Context context, PeopleFlowMember member) {
109         List<ActionRequestValue> roleMemberRequests = new ArrayList<ActionRequestValue>(); // results
110 
111         List<Map<String, String>> roleQualifierMaps = loadRoleQualifiers(context, member.getMemberId());
112 
113         // if we got no role qualifier maps, this means we need to generate a single request without any qualifiers.
114         // Use a list with one empty map in it to force adding a role request without qualifiers in the loop below.
115         if (CollectionUtils.isEmpty(roleQualifierMaps)) {
116             roleQualifierMaps = Collections.singletonList((Map<String,String>)Collections.EMPTY_MAP);
117         }
118 
119         Role role = getRoleService().getRole(member.getMemberId());
120 
121         boolean hasPeopleFlowDelegates = !CollectionUtils.isEmpty(member.getDelegates());
122 
123         if (role == null) {
124             throw new IllegalStateException("Failed to locate a role with the given role id of '" +
125                     member.getMemberId() + "'");
126         }
127 
128         for (Map<String, String> roleQualifiers : roleQualifierMaps) {
129             ActionRequestValue request = addKimRoleRequest(context, member, role, roleQualifiers,
130                     hasPeopleFlowDelegates);
131 
132             if (request != null) {
133                 roleMemberRequests.add(request);
134                 generateDelegationRequestsForRoleMember(context, request, member, roleQualifiers);
135             }
136         }
137 
138         return roleMemberRequests;
139     }
140 
141     /**
142      * Uses the ActionRequestFactory to build the request tree for the role members.
143      *
144      * <p>The role members themselves are derived here using the given qualifiers.</p>
145      *
146      * @param context the context for request generation
147      * @param member the PeopleFlow member
148      * @param role the role specified within the member
149      * @param roleQualifiers the qualifiers to use for role member selection
150      * @param ignoreKimDelegates should KIM delegates be ignored when generating requests?
151      * @return the root request of the generated action request tree, or null if no members are found
152      */
153     private ActionRequestValue addKimRoleRequest(Context context, PeopleFlowMember member, Role role,
154             Map<String, String> roleQualifiers, boolean ignoreKimDelegates) {
155 
156         ActionRequestValue roleMemberRequest = null;
157 
158         List<RoleMembership> memberships = getRoleService().getRoleMembers(Collections.singletonList(
159                 member.getMemberId()), roleQualifiers);
160 
161         String actionRequestPolicyCode = getActionRequestPolicyCode(member);
162 
163         if (!CollectionUtils.isEmpty(memberships)) {
164             roleMemberRequest = context.getActionRequestFactory().addKimRoleRequest(
165                     context.getActionRequested().getCode(), member.getPriority(), role, memberships, null,
166                     member.getResponsibilityId(), member.getForceAction(), actionRequestPolicyCode, null, ignoreKimDelegates);
167         }
168 
169         return roleMemberRequest;
170     }
171 
172     /**
173      * Generates any needed requests for {@link PeopleFlowDelegate}s on the given non-Role member.
174      *
175      * <p>If there are no delegates, or if no requests were generated for the member, then this will be a no-op.</p>
176      *
177      * @param context the context for request generation
178      * @param memberRequests any action requests that were generated for the given member
179      * @param member the PeopleFlow member
180      * @throws org.kuali.rice.core.api.exception.RiceRuntimeException if member has memberType of ROLE.
181      */
182     private void generateDelegationRequests(Context context, List<ActionRequestValue> memberRequests,
183             PeopleFlowMember member) {
184 
185         if (MemberType.ROLE == member.getMemberType()) {
186             throw new RiceRuntimeException("delegation for Role members");
187         }
188 
189         if (CollectionUtils.isEmpty(member.getDelegates()) || CollectionUtils.isEmpty(memberRequests)) {
190             return;
191         }
192 
193         for (PeopleFlowDelegate delegate : member.getDelegates()) {
194             for (ActionRequestValue memberRequest : memberRequests) {
195                 if (MemberType.ROLE != delegate.getMemberType()) {
196                     generateDelegationToNonRoleRequest(context, memberRequest, member, delegate);
197                 } else {
198                     generateDelegationToRoleRequests(context, memberRequest, member, delegate);
199                 }
200             }
201         }
202     }
203 
204     /**
205      * Uses the ActionRequestFactory to add the delegate request to the given parent request.
206      *
207      * <p>Only handles non-role delegates.  If a delegate of type role is passed, a RiceIllegalStateException will be
208      * thrown.</p>
209      *
210      * @param context the context for request generation
211      * @param memberRequest an action request that was generated for the given member
212      * @param member the PeopleFlow member
213      * @param delegate the delegate to generate a request to
214      */
215     private void generateDelegationToNonRoleRequest(Context context, ActionRequestValue memberRequest,
216             PeopleFlowMember member, PeopleFlowDelegate delegate) {
217 
218         Recipient recipient;
219 
220         if (MemberType.PRINCIPAL == delegate.getMemberType()) {
221             recipient = new KimPrincipalRecipient(delegate.getMemberId());
222         } else if (MemberType.GROUP == delegate.getMemberType()) {
223             recipient = new KimGroupRecipient(delegate.getMemberId());
224         } else {
225             throw new RiceIllegalStateException("MemberType unknown: " + delegate.getMemberType());
226         }
227 
228         String delegationAnnotation = generateDelegationAnnotation(memberRequest, member, delegate);
229 
230         context.getActionRequestFactory().addDelegationRequest(memberRequest, recipient,
231                 delegate.getResponsibilityId(), member.getForceAction(),
232                 delegate.getDelegationType(), delegationAnnotation, null);
233     }
234 
235     /**
236      * Generates any needed requests for the given {@link PeopleFlowDelegate}.
237      *
238      * <p>It is assumed that the given member is a Role.</p>
239      *
240      * @param context the context for request generation
241      * @param parentRequest an action request that was generated for the given member
242      * @param member the PeopleFlow member, which should contain the given delegate
243      * @param roleQualifiers member's qualifiers
244      */
245     protected void generateDelegationRequestsForRoleMember(Context context, ActionRequestValue parentRequest,
246             PeopleFlowMember member, Map<String, String> roleQualifiers) {
247 
248         if (CollectionUtils.isEmpty(member.getDelegates())) {
249             return;
250         }
251 
252         // To apply delegates to a role request, you have to dig out the non-role child requests and generate the
253         // delegates on each of them.  Otherwise you get a malformed action request tree that doesn't function correctly.
254         List<ActionRequestValue> nonRoleRequests = findNonRoleRequests(Collections.singletonList(parentRequest));
255 
256         if (CollectionUtils.isEmpty(nonRoleRequests)) {
257             return;
258         }
259 
260         for (ActionRequestValue nonRoleRequest : nonRoleRequests) {
261             generateDelegationRequestsForRoleMemberRequest(context, nonRoleRequest, member, roleQualifiers);
262         }
263     }
264 
265     /**
266      * Generates any needed requests for the given {@link PeopleFlowDelegate}.
267      *
268      * <p>It is assumed that the given member is a Role.</p>
269      *
270      * @param context the context for request generation
271      * @param roleRequestChild a <em>non-role</em> action request that was generated for the given Role member
272      * @param member the PeopleFlow member, which should contain the given delegate
273      * @param roleQualifier member's qualifier
274      */
275     private void generateDelegationRequestsForRoleMemberRequest(Context context, ActionRequestValue roleRequestChild,
276             PeopleFlowMember member, Map<String, String> roleQualifier) {
277 
278         for (PeopleFlowDelegate delegate : member.getDelegates()) {
279             // we're handling all delegation requests for Role members here, so we need to handle all delegate member types
280             if (MemberType.ROLE.equals(delegate.getMemberType())) {
281                 Role delegateRole = getRoleService().getRole(delegate.getMemberId());
282 
283                 if (delegateRole != null) {
284                     addKimRoleDelegateRequest(context, roleRequestChild, member, delegate, delegateRole,
285                             roleQualifier);
286                 }
287             } else {
288                 generateDelegationToNonRoleRequest(context, roleRequestChild, member, delegate);
289             }
290         }
291     }
292 
293     /**
294      * Builds the String that will be used for the annotation on the delegate requests
295      *
296      * @param parentRequest an action request that was generated for the given member
297      * @param member the PeopleFlow member
298      * @param delegate the delegate
299      * @return the annotation string
300      */
301     private String generateDelegationAnnotation(ActionRequestValue parentRequest, PeopleFlowMember member,
302             PeopleFlowDelegate delegate) {
303 
304         StringBuffer annotation = new StringBuffer( "Delegation of: " );
305         annotation.append( parentRequest.getAnnotation() );
306         annotation.append( " to " );
307 
308         if (delegate.getMemberType() == MemberType.PRINCIPAL) {
309             annotation.append( "principal " );
310             Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(delegate.getMemberId());
311 
312             if ( principal != null ) {
313                 annotation.append( principal.getPrincipalName() );
314             } else {
315                 annotation.append( member.getMemberId() );
316             }
317         } else if (delegate.getMemberType() == MemberType.GROUP) {
318             annotation.append( "group " );
319             Group group = KimApiServiceLocator.getGroupService().getGroup(delegate.getMemberId());
320 
321             if ( group != null ) {
322                 annotation.append( group.getNamespaceCode() ).append( '/' ).append( group.getName() );
323             } else {
324                 annotation.append( member.getMemberId() );
325             }
326         } else {
327             annotation.append( "?????? '" );
328             annotation.append( member.getMemberId() );
329             annotation.append( "'" );
330         }
331 
332         return annotation.toString();
333     }
334 
335     /**
336      * Generates any needed requests for the given {@link PeopleFlowDelegate}.
337      *
338      * <p>It is assumed that the given member has the given delegate configured.</p>
339      *
340      * @param context the context for request generation
341      * @param parentRequest an action request that was generated for the given member
342      * @param member the PeopleFlow member, which should contain the given delegate
343      * @param delegate the delegate, assumed to be of MemberType ROLE, to generate a request to
344      */
345     protected void generateDelegationToRoleRequests(Context context,
346             ActionRequestValue parentRequest, PeopleFlowMember member, PeopleFlowDelegate delegate) {
347 
348         List<Map<String, String>> roleQualifierList = loadRoleQualifiers(context, delegate.getMemberId());
349         Role role = getRoleService().getRole(delegate.getMemberId());
350 
351         if (role == null) {
352             throw new IllegalStateException("Failed to locate a role with the given role id of '" +
353                     delegate.getMemberId() + "'");
354         }
355 
356         if (CollectionUtils.isEmpty(roleQualifierList)) {
357             addKimRoleDelegateRequest(context, parentRequest, member, delegate, role,
358                     Collections.<String, String>emptyMap());
359         } else {
360             for (Map<String, String> roleQualifiers : roleQualifierList) {
361                 addKimRoleDelegateRequest(context, parentRequest, member, delegate, role, roleQualifiers);
362             }
363         }
364     }
365 
366     /**
367      * Helper method uses the ActionRequestFactory to add to the parent request the delegation request(s) to a role.
368      *
369      * <p>The role members themselves are derived here using the given qualifiers.</p>
370      *
371      * @param context the context for request generation
372      * @param parentRequest an action request that was generated for the given member
373      * @param member the PeopleFlow member
374      * @param delegate the delegate to generate a request to
375      * @param role the role specified within the delegate
376      * @param roleQualifiers the qualifiers to use for role member selection
377      */
378     private void addKimRoleDelegateRequest(Context context, ActionRequestValue parentRequest,
379             PeopleFlowMember member, PeopleFlowDelegate delegate, Role role, Map<String, String> roleQualifiers) {
380 
381         // sanity check
382         if (delegate.getMemberType() != MemberType.ROLE) {
383             throw new RiceIllegalArgumentException("delegate's member type must be ROLE");
384         } else if (!delegate.getMemberId().equals(role.getId())) {
385             throw new RiceIllegalArgumentException("delegate's member id must match the given role's id");
386         }
387 
388         String actionRequestPolicyCode = getDelegateActionRequestPolicyCode(member, delegate);
389 
390         List<RoleMembership> memberships = getRoleService().getRoleMembers(Collections.singletonList(
391                 delegate.getMemberId()), roleQualifiers);
392 
393         if (!CollectionUtils.isEmpty(memberships)) {
394             context.getActionRequestFactory().addDelegateKimRoleRequest(parentRequest,
395                     delegate.getDelegationType(), context.getActionRequested().getCode(), member.getPriority(), role,
396                     memberships, null, delegate.getResponsibilityId(), member.getForceAction(), actionRequestPolicyCode, null);
397         }
398     }
399 
400     /**
401      * Uses the appropriate {@link PeopleFlowTypeService} to get the role qualifier maps for the given document and
402      * role.
403      *
404      * <p>Note that the PeopleFlowTypeService is selected based on the type id of the PeopleFlow.</p>
405      *
406      * @param context the context for request generation
407      * @param roleId the ID of the role for whom qualifiers are being loaded
408      * @return the qualifier maps, or an empty list if there are none
409      */
410     protected List<Map<String, String>> loadRoleQualifiers(Context context, String roleId) {
411         PeopleFlowTypeService peopleFlowTypeService = context.getPeopleFlowTypeService();
412         List<Map<String, String>> roleQualifierList = new ArrayList<Map<String, String>>();
413 
414         if (peopleFlowTypeService != null) {
415             Document document = DocumentRouteHeaderValue.to(context.getRouteContext().getDocument());
416             DocumentRouteHeaderValueContent content = new DocumentRouteHeaderValueContent(document.getDocumentId());
417             content.setDocumentContent(context.getRouteContext().getDocumentContent().getDocContent());
418             DocumentContent documentContent = DocumentRouteHeaderValueContent.to(content);
419 
420             Map<String, String> roleQualifiers = peopleFlowTypeService.resolveRoleQualifiers(
421                     context.getPeopleFlow().getTypeId(), roleId, document, documentContent
422             );
423 
424             if (roleQualifiers != null) {
425                 roleQualifierList.add(roleQualifiers);
426             }
427 
428             boolean versionOk = VersionHelper.compareVersion(context.getPeopleFlowTypeServiceVersion(), CoreConstants.Versions.VERSION_2_3_0) != -1;
429             if(versionOk) {
430                 List<Map<String, String>> multipleRoleQualifiers = peopleFlowTypeService.resolveMultipleRoleQualifiers(
431                         context.getPeopleFlow().getTypeId(), roleId, document, documentContent);
432 
433                 if (multipleRoleQualifiers != null) {
434                     roleQualifierList.addAll(multipleRoleQualifiers);
435                 }
436             }
437 
438         }
439 
440         return roleQualifierList;
441     }
442 
443     /**
444      * Gets the action request policy code for the given delegate.
445      *
446      * <p>the delegate is considered first, and the member is used as a fallback.  May return null.</p>
447      *
448      * @param member the PeopleFlow member
449      * @param delegate the delegate
450      * @return the action request policy code, or null if none is found
451      */
452     private String getDelegateActionRequestPolicyCode(PeopleFlowMember member, PeopleFlowDelegate delegate) {
453         ActionRequestPolicy actionRequestPolicy = delegate.getActionRequestPolicy();
454 
455         return (actionRequestPolicy != null) ? actionRequestPolicy.getCode() : getActionRequestPolicyCode(member);
456     }
457 
458     /**
459      * Gets the action request policy code for the given member.
460      *
461      * @param member the PeopleFlow member
462      * @return the action request policy code, or null if none is found
463      */
464     private String getActionRequestPolicyCode(PeopleFlowMember member) {
465         ActionRequestPolicy actionRequestPolicy = member.getActionRequestPolicy();
466 
467         return (actionRequestPolicy != null) ? actionRequestPolicy.getCode() : null;
468     }
469 
470     private Recipient toRecipient(PeopleFlowMember member) {
471         Recipient recipient;
472         if (MemberType.PRINCIPAL == member.getMemberType()) {
473             recipient = new KimPrincipalRecipient(member.getMemberId());
474         } else if (MemberType.GROUP == member.getMemberType()) {
475             recipient = new KimGroupRecipient(member.getMemberId());
476         } else {
477             throw new IllegalStateException("encountered a member type which I did not understand: " +
478                     member.getMemberType());
479         }
480         return recipient;
481     }
482 
483     private Recipient toRecipient(PeopleFlowDelegate delegate) {
484         Recipient recipient;
485         if (MemberType.PRINCIPAL == delegate.getMemberType()) {
486             recipient = new KimPrincipalRecipient(delegate.getMemberId());
487         } else if (MemberType.GROUP == delegate.getMemberType()) {
488             recipient = new KimGroupRecipient(delegate.getMemberId());
489         } else {
490             throw new IllegalStateException("encountered a delegate member type which I did not understand: " +
491                     delegate.getMemberType());
492         }
493         return recipient;
494     }
495 
496     public KewTypeRepositoryService getTypeRepositoryService() {
497         return typeRepositoryService;
498     }
499 
500     public void setTypeRepositoryService(KewTypeRepositoryService typeRepositoryService) {
501         this.typeRepositoryService = typeRepositoryService;
502     }
503 
504     public RoleService getRoleService() {
505         return roleService;
506     }
507 
508     public void setRoleService(RoleService roleService) {
509         this.roleService = roleService;
510     }
511 
512     /**
513      * Recursively find all non-delegate Group and Principal requests from all of the requests in the given list.
514      *
515      * @param actionRequestValues the list of {@link ActionRequestValue}s to search
516      * @return a list of the non-delegate Group and Principal requests found
517      */
518     private List<ActionRequestValue> findNonRoleRequests(List<ActionRequestValue> actionRequestValues) {
519         List<ActionRequestValue> nonRoleRequests = new ArrayList<ActionRequestValue>();
520 
521         return findNonRoleRequests(actionRequestValues, nonRoleRequests);
522     }
523 
524     // Recursion helper method
525     private List<ActionRequestValue> findNonRoleRequests(List<ActionRequestValue> actionRequestValues,
526             List<ActionRequestValue> nonRoleRequests) {
527 
528         if (!CollectionUtils.isEmpty(actionRequestValues)) {
529             for (ActionRequestValue request : actionRequestValues) if (request.getDelegationType() == null) {
530                 if (!CollectionUtils.isEmpty(request.getChildrenRequests())) {
531                     findNonRoleRequests(request.getChildrenRequests(), nonRoleRequests);
532                 } else  {
533                     // see if we have a principal request
534                     if (RecipientType.ROLE.getCode() != request.getRecipientTypeCd()) {
535                         nonRoleRequests.add(request);
536                     }
537                 }
538             }
539         }
540 
541         return nonRoleRequests;
542     }
543 
544 
545     /**
546      * A simple class used to hold context during the PeopleFlow action request generation process.  Construction of
547      * the context will validate that the given values are valid, non-null values where appropriate.
548      */
549     final class Context {
550 
551         private final RouteContext routeContext;
552         private final PeopleFlowDefinition peopleFlow;
553         private final ActionRequestType actionRequested;
554         private final ActionRequestFactory actionRequestFactory;
555 
556         // lazily loaded
557         private PeopleFlowTypeService peopleFlowTypeService;
558         private boolean peopleFlowTypeServiceLoaded = false;
559         private String peopleFlowTypeServiceVersion;
560 
561         Context(RouteContext routeContext, PeopleFlowDefinition peopleFlow, ActionRequestType actionRequested) {
562             if (routeContext == null) {
563                 throw new IllegalArgumentException("routeContext was null");
564             }
565             if (peopleFlow == null) {
566                 throw new IllegalArgumentException("peopleFlow was null");
567             }
568             if (!peopleFlow.isActive()) {
569                 throw new ConfigurationException("Attempted to route to a PeopleFlow that is not active! " + peopleFlow);
570             }
571             if (actionRequested == null) {
572                 actionRequested = ActionRequestType.APPROVE;
573             }
574             this.routeContext = routeContext;
575             this.peopleFlow = peopleFlow;
576             this.actionRequested = actionRequested;
577             this.actionRequestFactory = new ActionRequestFactory(routeContext);
578         }
579 
580         RouteContext getRouteContext() {
581             return routeContext;
582         }
583 
584         PeopleFlowDefinition getPeopleFlow() {
585             return peopleFlow;
586         }
587 
588         ActionRequestType getActionRequested() {
589             return actionRequested;
590         }
591 
592         ActionRequestFactory getActionRequestFactory() {
593             return actionRequestFactory;
594         }
595 
596         /**
597          * Lazily loads and caches the {@code PeopleFlowTypeService} (if necessary) and returns it.
598          */
599         PeopleFlowTypeService getPeopleFlowTypeService() {
600 
601             if (peopleFlowTypeServiceLoaded) {
602                 return this.peopleFlowTypeService;
603             }
604 
605             if (getPeopleFlow().getTypeId() != null) {
606                 KewTypeDefinition typeDefinition = getTypeRepositoryService().getTypeById(getPeopleFlow().getTypeId());
607 
608                 if (typeDefinition == null) {
609                     throw new IllegalStateException("Failed to locate a PeopleFlow type for the given type id of '" + getPeopleFlow().getTypeId() + "'");
610                 }
611 
612                 if (StringUtils.isNotBlank(typeDefinition.getServiceName())) {
613                     Endpoint endpoint = KsbApiServiceLocator.getServiceBus().getEndpoint(QName.valueOf(typeDefinition.getServiceName()));
614 
615                     if (endpoint == null) {
616                         throw new IllegalStateException("Failed to load the PeopleFlowTypeService with the name '" + typeDefinition.getServiceName() + "'");
617                     }
618 
619                     this.peopleFlowTypeService = (PeopleFlowTypeService)endpoint.getService();
620                     this.peopleFlowTypeServiceVersion = endpoint.getServiceConfiguration().getServiceVersion();
621                 }
622             }
623             peopleFlowTypeServiceLoaded = true;
624             return this.peopleFlowTypeService;
625         }
626 
627         String getPeopleFlowTypeServiceVersion() {
628             if (!this.peopleFlowTypeServiceLoaded) {
629                 // execute getPeopleFlowTypeService first to lazy load
630                 getPeopleFlowTypeService();
631             }
632 
633             return this.peopleFlowTypeServiceVersion;
634         }
635     }
636 }