001/**
002 * Copyright 2004-2014 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            }
132        } else {
133            // we may have multiple maps of role qualifiers, so we'll add a request for each map
134            for (Map<String, String> roleQualifiers : roleQualifierMaps) {
135                ActionRequestValue request = addKimRoleRequest(context, member, role, roleQualifiers,
136                        hasPeopleFlowDelegates);
137                if (request != null) {
138                    roleMemberRequests.add(request);
139                }
140            }
141        }
142
143        return roleMemberRequests;
144    }
145
146    /**
147     * Uses the ActionRequestFactory to build the request tree for the role members.
148     *
149     * <p>The role members themselves are derived here using the given qualifiers.</p>
150     *
151     * @param context the context for request generation
152     * @param member the PeopleFlow member
153     * @param role the role specified within the member
154     * @param roleQualifiers the qualifiers to use for role member selection
155     * @param ignoreKimDelegates should KIM delegates be ignored when generating requests?
156     * @return the root request of the generated action request tree, or null if no members are found
157     */
158    private ActionRequestValue addKimRoleRequest(Context context, PeopleFlowMember member, Role role,
159                                                 Map<String, String> roleQualifiers, boolean ignoreKimDelegates) {
160
161        ActionRequestValue roleMemberRequest = null;
162
163        List<RoleMembership> memberships = getRoleService().getRoleMembers(Collections.singletonList(
164                member.getMemberId()), roleQualifiers);
165
166        String actionRequestPolicyCode = getActionRequestPolicyCode(member);
167
168        if (!CollectionUtils.isEmpty(memberships)) {
169            roleMemberRequest = context.getActionRequestFactory().addKimRoleRequest(
170                    context.getActionRequested().getCode(), member.getPriority(), role, memberships, null,
171                    member.getResponsibilityId(), true, actionRequestPolicyCode, null, ignoreKimDelegates);
172            //memberships.get(0).getQualifier()
173        }
174
175        return roleMemberRequest;
176    }
177
178    /**
179     * Generates any needed requests for {@link PeopleFlowDelegate}s on the given member.
180     *
181     * <p>If there are no delegates, or if no requests were generated for the member, then this will be a no-op.</p>
182     *
183     * @param context the context for request generation
184     * @param memberRequests any action requests that were generated for the given member
185     * @param member the PeopleFlow member
186     */
187    private void generateDelegationRequests(Context context, List<ActionRequestValue> memberRequests,
188                                            PeopleFlowMember member) {
189
190        if (CollectionUtils.isEmpty(member.getDelegates()) || CollectionUtils.isEmpty(memberRequests)) {
191            return;
192        }
193
194        /*for (PeopleFlowDelegate delegate : member.getDelegates()) {
195            if (MemberType.ROLE == delegate.getMemberType()) {
196                generateDelegationToRoleRequests(context, memberRequest, member, delegate);
197            } else {
198                for (ActionRequestValue memberRequest : memberRequests) {
199                    generateDelegationToNonRoleRequest(context, memberRequest, member, delegate);
200                }
201            }
202        }*/
203        /*List<RoleMembership> memberships = getRoleService().getRoleMembers(Collections.singletonList(
204                member.getMemberId()), roleQualifiers);*/
205        for (PeopleFlowDelegate delegate : member.getDelegates()) {
206            for (ActionRequestValue memberRequest : memberRequests) {
207                if (MemberType.ROLE == delegate.getMemberType()) {
208                    generateDelegationToRoleRequests(context, memberRequest, member, delegate);
209                } else {
210                    generateDelegationToNonRoleRequest(context, memberRequest, member, delegate);
211                }
212            }
213        }
214    }
215
216    /**
217     * Uses the ActionRequestFactory to add the delegate request to the given parent request.
218     *
219     * <p>Only handles non-role delegates.  If a delegate of type role is passed, a RiceIllegalStateException will be
220     * thrown.</p>
221     *
222     * @param context the context for request generation
223     * @param memberRequest an action request that was generated for the given member
224     * @param member the PeopleFlow member
225     * @param delegate the delegate to generate a request to
226     */
227    private void generateDelegationToNonRoleRequest(Context context, ActionRequestValue memberRequest,
228                                                    PeopleFlowMember member, PeopleFlowDelegate delegate) {
229
230        Recipient recipient;
231
232        if (MemberType.PRINCIPAL == delegate.getMemberType()) {
233            recipient = new KimPrincipalRecipient(delegate.getMemberId());
234        } else if (MemberType.GROUP == delegate.getMemberType()) {
235            recipient = new KimGroupRecipient(delegate.getMemberId());
236        } else {
237            throw new RiceIllegalStateException("MemberType unknown: " + delegate.getMemberType());
238        }
239
240        String actionRequestPolicyCode = getDelegateActionRequestPolicyCode(member, delegate);
241
242        String delegationAnnotation = generateDelegationAnnotation(memberRequest, member, delegate);
243
244        context.getActionRequestFactory().addDelegationRequest(memberRequest, recipient,
245                delegate.getResponsibilityId(), memberRequest.getForceAction(),
246                delegate.getDelegationType(), actionRequestPolicyCode, delegationAnnotation, null);
247    }
248
249    /**
250     * Builds the String that will be used for the annotation on the delegate requests
251     *
252     * @param parentRequest an action request that was generated for the given member
253     * @param member the PeopleFlow member
254     * @param delegate the delegate
255     * @return the annotation string
256     */
257    private String generateDelegationAnnotation(ActionRequestValue parentRequest, PeopleFlowMember member,
258                                                PeopleFlowDelegate delegate) {
259
260        StringBuffer annotation = new StringBuffer( "Delegation of: " );
261        annotation.append( parentRequest.getAnnotation() );
262        annotation.append( " to " );
263
264        if (delegate.getMemberType() == MemberType.PRINCIPAL) {
265            annotation.append( "principal " );
266            Principal principal = KimApiServiceLocator.getIdentityService().getPrincipal(delegate.getMemberId());
267
268            if ( principal != null ) {
269                annotation.append( principal.getPrincipalName() );
270            } else {
271                annotation.append( member.getMemberId() );
272            }
273        } else if (delegate.getMemberType() == MemberType.GROUP) {
274            annotation.append( "group " );
275            Group group = KimApiServiceLocator.getGroupService().getGroup(delegate.getMemberId());
276
277            if ( group != null ) {
278                annotation.append( group.getNamespaceCode() ).append( '/' ).append( group.getName() );
279            } else {
280                annotation.append( member.getMemberId() );
281            }
282        } else {
283            annotation.append( "?????? '" );
284            annotation.append( member.getMemberId() );
285            annotation.append( "'" );
286        }
287
288        return annotation.toString();
289    }
290
291
292    /**
293     * Generates any needed requests for the given {@link PeopleFlowDelegate}.
294     *
295     * <p>It is assumed that the given member has the given delegate configured.</p>
296     *
297     * @param context the context for request generation
298     * @param parentRequest an action request that was generated for the given member
299     * @param member the PeopleFlow member, which should contain the given delegate
300     * @param delegate the delegate, assumed to be of MemberType ROLE, to generate a request to
301     */
302    protected void generateDelegationToRoleRequests(Context context,
303                                                    ActionRequestValue parentRequest, PeopleFlowMember member, PeopleFlowDelegate delegate) {
304
305        List<Map<String, String>> roleQualifierList = loadRoleQualifiers(context, delegate.getMemberId());
306        Role role = getRoleService().getRole(delegate.getMemberId());
307
308        if (role == null) {
309            throw new IllegalStateException("Failed to locate a role with the given role id of '" +
310                    delegate.getMemberId() + "'");
311        }
312
313        if (CollectionUtils.isEmpty(roleQualifierList)) {
314            addKimRoleDelegateRequest(context, parentRequest, member, delegate, role,
315                    Collections.<String, String>emptyMap());
316        } else {
317            for (Map<String, String> roleQualifiers : roleQualifierList) {
318                List<RoleMembership> memberships = getRoleService().getRoleMembers(Collections.singletonList(
319                        member.getMemberId()), roleQualifiers);
320                for (RoleMembership membership : memberships) {
321                    if (membership.getType().equals(MemberType.PRINCIPAL)
322                            && StringUtils.equals(membership.getMemberId(), parentRequest.getPrincipalId())) {
323                        addKimRoleDelegateRequest(context, parentRequest, member, delegate, role, roleQualifiers);
324                    } else if (membership.getType().equals(MemberType.GROUP)
325                            && StringUtils.equals(membership.getMemberId(), parentRequest.getGroupId())) {
326                        addKimRoleDelegateRequest(context, parentRequest, member, delegate, role, roleQualifiers);
327                    }
328                }
329
330            }
331        }
332    }
333
334    /**
335     * Helper method uses the ActionRequestFactory to add to the parent request the delegation request(s) to a role.
336     *
337     * <p>The role members themselves are derived here using the given qualifiers.</p>
338     *
339     * @param context the context for request generation
340     * @param parentRequest an action request that was generated for the given member
341     * @param member the PeopleFlow member
342     * @param delegate the delegate to generate a request to
343     * @param role the role specified within the delegate
344     * @param roleQualifiers the qualifiers to use for role member selection
345     */
346    private void addKimRoleDelegateRequest(Context context, ActionRequestValue parentRequest,
347                                           PeopleFlowMember member, PeopleFlowDelegate delegate, Role role, Map<String, String> roleQualifiers) {
348
349        // sanity check
350        if (delegate.getMemberType() != MemberType.ROLE) {
351            throw new RiceIllegalArgumentException("delegate's member type must be ROLE");
352        } else if (!delegate.getMemberId().equals(role.getId())) {
353            throw new RiceIllegalArgumentException("delegate's member id must match the given role's id");
354        }
355
356        String actionRequestPolicyCode = getDelegateActionRequestPolicyCode(member, delegate);
357
358        List<RoleMembership> memberships = getRoleService().getRoleMembers(Collections.singletonList(
359                delegate.getMemberId()), roleQualifiers);
360
361        if (!CollectionUtils.isEmpty(memberships)) {
362            context.getActionRequestFactory().addDelegateKimRoleRequest(parentRequest,
363                    delegate.getDelegationType(), context.getActionRequested().getCode(), member.getPriority(), role,
364                    memberships, null, delegate.getResponsibilityId(), true, actionRequestPolicyCode, null);
365        }
366    }
367
368    /**
369     * Uses the appropriate {@link PeopleFlowTypeService} to get the role qualifier maps for the given document and
370     * role.
371     *
372     * <p>Note that the PeopleFlowTypeService is selected based on the type id of the PeopleFlow.</p>
373     *
374     * @param context the context for request generation
375     * @param roleId the ID of the role for whom qualifiers are being loaded
376     * @return the qualifier maps, or an empty list if there are none
377     */
378    protected List<Map<String, String>> loadRoleQualifiers(Context context, String roleId) {
379        PeopleFlowTypeService peopleFlowTypeService = context.getPeopleFlowTypeService();
380        List<Map<String, String>> roleQualifierList = new ArrayList<Map<String, String>>();
381
382        if (peopleFlowTypeService != null) {
383            Document document = DocumentRouteHeaderValue.to(context.getRouteContext().getDocument());
384            DocumentRouteHeaderValueContent content = new DocumentRouteHeaderValueContent(document.getDocumentId());
385            content.setDocumentContent(context.getRouteContext().getDocumentContent().getDocContent());
386            DocumentContent documentContent = DocumentRouteHeaderValueContent.to(content);
387
388            Map<String, String> roleQualifiers = peopleFlowTypeService.resolveRoleQualifiers(
389                    context.getPeopleFlow().getTypeId(), roleId, document, documentContent
390            );
391
392            if (roleQualifiers != null) {
393                roleQualifierList.add(roleQualifiers);
394            }
395
396            boolean versionOk = VersionHelper.compareVersion(context.getPeopleFlowTypeServiceVersion(), CoreConstants.Versions.VERSION_2_3_0) != -1;
397            if(versionOk) {
398                List<Map<String, String>> multipleRoleQualifiers = peopleFlowTypeService.resolveMultipleRoleQualifiers(
399                        context.getPeopleFlow().getTypeId(), roleId, document, documentContent);
400
401                if (multipleRoleQualifiers != null) {
402                    roleQualifierList.addAll(multipleRoleQualifiers);
403                }
404            }
405
406        }
407
408        return roleQualifierList;
409    }
410
411    /**
412     * Gets the action request policy code for the given delegate.
413     *
414     * <p>the delegate is considered first, and the member is used as a fallback.  May return null.</p>
415     *
416     * @param member the PeopleFlow member
417     * @param delegate the delegate
418     * @return the action request policy code, or null if none is found
419     */
420    private String getDelegateActionRequestPolicyCode(PeopleFlowMember member, PeopleFlowDelegate delegate) {
421        ActionRequestPolicy actionRequestPolicy = delegate.getActionRequestPolicy();
422
423        return (actionRequestPolicy != null) ? actionRequestPolicy.getCode() : getActionRequestPolicyCode(member);
424    }
425
426    /**
427     * Gets the action request policy code for the given member.
428     *
429     * @param member the PeopleFlow member
430     * @return the action request policy code, or null if none is found
431     */
432    private String getActionRequestPolicyCode(PeopleFlowMember member) {
433        ActionRequestPolicy actionRequestPolicy = member.getActionRequestPolicy();
434
435        return (actionRequestPolicy != null) ? actionRequestPolicy.getCode() : null;
436    }
437
438    private Recipient toRecipient(PeopleFlowMember member) {
439        Recipient recipient;
440        if (MemberType.PRINCIPAL == member.getMemberType()) {
441            recipient = new KimPrincipalRecipient(member.getMemberId());
442        } else if (MemberType.GROUP == member.getMemberType()) {
443            recipient = new KimGroupRecipient(member.getMemberId());
444        } else {
445            throw new IllegalStateException("encountered a member type which I did not understand: " +
446                    member.getMemberType());
447        }
448        return recipient;
449    }
450
451    private Recipient toRecipient(PeopleFlowDelegate delegate) {
452        Recipient recipient;
453        if (MemberType.PRINCIPAL == delegate.getMemberType()) {
454            recipient = new KimPrincipalRecipient(delegate.getMemberId());
455        } else if (MemberType.GROUP == delegate.getMemberType()) {
456            recipient = new KimGroupRecipient(delegate.getMemberId());
457        } else {
458            throw new IllegalStateException("encountered a delegate member type which I did not understand: " +
459                    delegate.getMemberType());
460        }
461        return recipient;
462    }
463
464    public KewTypeRepositoryService getTypeRepositoryService() {
465        return typeRepositoryService;
466    }
467
468    public void setTypeRepositoryService(KewTypeRepositoryService typeRepositoryService) {
469        this.typeRepositoryService = typeRepositoryService;
470    }
471
472    public RoleService getRoleService() {
473        return roleService;
474    }
475
476    public void setRoleService(RoleService roleService) {
477        this.roleService = roleService;
478    }
479
480    /**
481     * Recursively find all non-delegate Group and Principal requests from all of the requests in the given list.
482     *
483     * @param actionRequestValues the list of {@link ActionRequestValue}s to search
484     * @return a list of the non-delegate Group and Principal requests found
485     */
486    private List<ActionRequestValue> findNonRoleRequests(List<ActionRequestValue> actionRequestValues) {
487        List<ActionRequestValue> nonRoleRequests = new ArrayList<ActionRequestValue>();
488
489        return findNonRoleRequests(actionRequestValues, nonRoleRequests);
490    }
491
492    // Recursion helper method
493    private List<ActionRequestValue> findNonRoleRequests(List<ActionRequestValue> actionRequestValues,
494                                                         List<ActionRequestValue> nonRoleRequests) {
495
496        if (!CollectionUtils.isEmpty(actionRequestValues)) {
497            for (ActionRequestValue request : actionRequestValues) if (request.getDelegationType() == null) {
498                if (!CollectionUtils.isEmpty(request.getChildrenRequests())) {
499                    findNonRoleRequests(request.getChildrenRequests(), nonRoleRequests);
500                } else  {
501                    // see if we have a principal request
502                    if (RecipientType.ROLE.getCode() != request.getRecipientTypeCd()) {
503                        nonRoleRequests.add(request);
504                    }
505                }
506            }
507        }
508
509        return nonRoleRequests;
510    }
511
512
513    /**
514     * A simple class used to hold context during the PeopleFlow action request generation process.  Construction of
515     * the context will validate that the given values are valid, non-null values where appropriate.
516     */
517    final class Context {
518
519        private final RouteContext routeContext;
520        private final PeopleFlowDefinition peopleFlow;
521        private final ActionRequestType actionRequested;
522        private final ActionRequestFactory actionRequestFactory;
523
524        // lazily loaded
525        private PeopleFlowTypeService peopleFlowTypeService;
526        private boolean peopleFlowTypeServiceLoaded = false;
527        private String peopleFlowTypeServiceVersion;
528
529        Context(RouteContext routeContext, PeopleFlowDefinition peopleFlow, ActionRequestType actionRequested) {
530            if (routeContext == null) {
531                throw new IllegalArgumentException("routeContext was null");
532            }
533            if (peopleFlow == null) {
534                throw new IllegalArgumentException("peopleFlow was null");
535            }
536            if (!peopleFlow.isActive()) {
537                throw new ConfigurationException("Attempted to route to a PeopleFlow that is not active! " + peopleFlow);
538            }
539            if (actionRequested == null) {
540                actionRequested = ActionRequestType.APPROVE;
541            }
542            this.routeContext = routeContext;
543            this.peopleFlow = peopleFlow;
544            this.actionRequested = actionRequested;
545            this.actionRequestFactory = new ActionRequestFactory(routeContext);
546        }
547
548        RouteContext getRouteContext() {
549            return routeContext;
550        }
551
552        PeopleFlowDefinition getPeopleFlow() {
553            return peopleFlow;
554        }
555
556        ActionRequestType getActionRequested() {
557            return actionRequested;
558        }
559
560        ActionRequestFactory getActionRequestFactory() {
561            return actionRequestFactory;
562        }
563
564        /**
565         * Lazily loads and caches the {@code PeopleFlowTypeService} (if necessary) and returns it.
566         */
567        PeopleFlowTypeService getPeopleFlowTypeService() {
568            if (peopleFlowTypeServiceLoaded) {
569                return this.peopleFlowTypeService;
570            }
571
572            if (getPeopleFlow().getTypeId() != null) {
573                KewTypeDefinition typeDefinition = getTypeRepositoryService().getTypeById(getPeopleFlow().getTypeId());
574
575                if (typeDefinition == null) {
576                    throw new IllegalStateException("Failed to locate a PeopleFlow type for the given type id of '" + getPeopleFlow().getTypeId() + "'");
577                }
578
579                if (StringUtils.isNotBlank(typeDefinition.getServiceName())) {
580                    Endpoint endpoint = KsbApiServiceLocator.getServiceBus().getEndpoint(QName.valueOf(typeDefinition.getServiceName()));
581
582                    if (endpoint == null) {
583                        throw new IllegalStateException("Failed to load the PeopleFlowTypeService with the name '" + typeDefinition.getServiceName() + "'");
584                    }
585
586                    this.peopleFlowTypeService = (PeopleFlowTypeService)endpoint.getService();
587                    this.peopleFlowTypeServiceVersion = endpoint.getServiceConfiguration().getServiceVersion();
588                }
589            }
590            peopleFlowTypeServiceLoaded = true;
591            return this.peopleFlowTypeService;
592        }
593
594        String getPeopleFlowTypeServiceVersion() {
595            if (!this.peopleFlowTypeServiceLoaded) {
596                // execute getPeopleFlowTypeService first to lazy load
597                getPeopleFlowTypeService();
598            }
599
600            return this.peopleFlowTypeServiceVersion;
601        }
602    }
603}