001/**
002 * Copyright 2004-2015 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kew.impl.peopleflow;
017
018import org.apache.commons.collections.CollectionUtils;
019import org.apache.commons.lang.StringUtils;
020import org.kuali.rice.core.api.CoreConstants;
021import org.kuali.rice.core.api.config.ConfigurationException;
022import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
023import org.kuali.rice.core.api.exception.RiceIllegalStateException;
024import org.kuali.rice.core.api.membership.MemberType;
025import org.kuali.rice.core.api.util.VersionHelper;
026import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
027import org.kuali.rice.kew.actionrequest.ActionRequestValue;
028import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
029import org.kuali.rice.kew.actionrequest.KimPrincipalRecipient;
030import org.kuali.rice.kew.actionrequest.Recipient;
031import org.kuali.rice.kew.api.action.ActionRequestPolicy;
032import org.kuali.rice.kew.api.action.ActionRequestType;
033import org.kuali.rice.kew.api.action.RecipientType;
034import org.kuali.rice.kew.api.document.Document;
035import org.kuali.rice.kew.api.document.DocumentContent;
036import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
037import org.kuali.rice.kew.api.peopleflow.PeopleFlowDelegate;
038import org.kuali.rice.kew.api.peopleflow.PeopleFlowMember;
039import org.kuali.rice.kew.api.repository.type.KewTypeDefinition;
040import org.kuali.rice.kew.api.repository.type.KewTypeRepositoryService;
041import org.kuali.rice.kew.engine.RouteContext;
042import org.kuali.rice.kew.framework.peopleflow.PeopleFlowTypeService;
043import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
044import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValueContent;
045import org.kuali.rice.kim.api.group.Group;
046import org.kuali.rice.kim.api.identity.principal.Principal;
047import org.kuali.rice.kim.api.role.Role;
048import org.kuali.rice.kim.api.role.RoleMembership;
049import org.kuali.rice.kim.api.role.RoleService;
050import org.kuali.rice.kim.api.services.KimApiServiceLocator;
051import org.kuali.rice.ksb.api.KsbApiServiceLocator;
052import org.kuali.rice.ksb.api.bus.Endpoint;
053
054import javax.xml.namespace.QName;
055import java.util.ArrayList;
056import java.util.Collections;
057import java.util.List;
058import java.util.Map;
059
060/**
061 * Reference implementation of the {@code PeopleFlowRequestGenerator} which is responsible for generating Action
062 * Requests from a {@link PeopleFlowDefinition}.
063 *
064 * @author Kuali Rice Team (rice.collab@kuali.org)
065 */
066public class PeopleFlowRequestGeneratorImpl implements PeopleFlowRequestGenerator {
067
068    private KewTypeRepositoryService typeRepositoryService;
069    private RoleService roleService;
070
071    @Override
072    public List<ActionRequestValue> generateRequests(RouteContext routeContext, PeopleFlowDefinition peopleFlow, ActionRequestType actionRequested) {
073        Context context = new Context(routeContext, peopleFlow, actionRequested);
074        for (PeopleFlowMember member : peopleFlow.getMembers()) {
075            generateRequestForMember(context, member);
076        }
077
078        return context.getActionRequestFactory().getRequestGraphs();
079    }
080
081    protected void generateRequestForMember(Context context, PeopleFlowMember member) {
082        // used later for generating any delegate requests
083        List<ActionRequestValue> memberRequests = new ArrayList<ActionRequestValue>();
084
085        if (MemberType.ROLE == member.getMemberType()) {
086            memberRequests.addAll(findNonRoleRequests(generateRequestsForRoleMember(context, member)));
087        } else {
088            ActionRequestValue actionRequest = context.getActionRequestFactory().addRootActionRequest(
089                    context.getActionRequested().getCode(), member.getPriority(), toRecipient(member), "",
090                    member.getResponsibilityId(), Boolean.TRUE, getActionRequestPolicyCode(member), null);
091
092            if (actionRequest != null) {
093                memberRequests.add(actionRequest);
094            }
095        }
096
097        // KULRICE-5726: Add support for delegates on roles in PeopleFlows as well as using roles as delegates
098        generateDelegationRequests(context, memberRequests, member);
099    }
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}