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