View Javadoc

1   /**
2    * Copyright 2005-2013 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.membership.MemberType;
23  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
24  import org.kuali.rice.core.api.util.VersionHelper;
25  import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
26  import org.kuali.rice.kew.actionrequest.ActionRequestValue;
27  import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
28  import org.kuali.rice.kew.actionrequest.KimPrincipalRecipient;
29  import org.kuali.rice.kew.actionrequest.Recipient;
30  import org.kuali.rice.kew.api.KewApiConstants;
31  import org.kuali.rice.kew.api.action.ActionRequestType;
32  import org.kuali.rice.kew.api.document.Document;
33  import org.kuali.rice.kew.api.document.DocumentContent;
34  import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
35  import org.kuali.rice.kew.api.peopleflow.PeopleFlowDelegate;
36  import org.kuali.rice.kew.api.peopleflow.PeopleFlowMember;
37  import org.kuali.rice.kew.api.repository.type.KewTypeDefinition;
38  import org.kuali.rice.kew.api.repository.type.KewTypeRepositoryService;
39  import org.kuali.rice.kew.engine.RouteContext;
40  import org.kuali.rice.kew.framework.peopleflow.PeopleFlowTypeService;
41  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
42  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValueContent;
43  import org.kuali.rice.kim.api.role.Role;
44  import org.kuali.rice.kim.api.role.RoleMembership;
45  import org.kuali.rice.kim.api.role.RoleService;
46  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
47  import org.kuali.rice.ksb.api.bus.Endpoint;
48  
49  import javax.xml.namespace.QName;
50  import java.util.ArrayList;
51  import java.util.Collections;
52  import java.util.List;
53  import java.util.Map;
54  
55  /**
56   * Reference implementation of the {@code PeopleFlowRequestGenerator} which is responsible for generating Action
57   * Requests from a {@link PeopleFlowDefinition}.
58   *
59   * @author Kuali Rice Team (rice.collab@kuali.org)
60   */
61  public class PeopleFlowRequestGeneratorImpl implements PeopleFlowRequestGenerator {
62  
63      private KewTypeRepositoryService typeRepositoryService;
64      private RoleService roleService;
65  
66      @Override
67      public List<ActionRequestValue> generateRequests(RouteContext routeContext, PeopleFlowDefinition peopleFlow, ActionRequestType actionRequested) {
68          Context context = new Context(routeContext, peopleFlow, actionRequested);
69          for (PeopleFlowMember member : peopleFlow.getMembers()) {
70              generateRequestForMember(context, member);
71          }
72          return context.getActionRequestFactory().getRequestGraphs();
73      }
74  
75      protected void generateRequestForMember(Context context, PeopleFlowMember member) {
76          String actionRequestPolicyCode = null;
77          if (member.getActionRequestPolicy() != null) {
78              actionRequestPolicyCode = member.getActionRequestPolicy().getCode();
79          }
80          if (MemberType.ROLE == member.getMemberType()) {
81              generateRequestForRoleMember(context, member, actionRequestPolicyCode);
82          } else {
83              ActionRequestValue actionRequest = context.getActionRequestFactory().addRootActionRequest(
84                      context.getActionRequested().getCode(), member.getPriority(), toRecipient(member), "",
85                      member.getResponsibilityId(), Boolean.TRUE, actionRequestPolicyCode, null);
86              if (CollectionUtils.isNotEmpty(member.getDelegates())) {
87                  for (PeopleFlowDelegate delegate : member.getDelegates()) {
88                      context.getActionRequestFactory().addDelegationRequest(actionRequest, toRecipient(delegate),
89                              delegate.getResponsibilityId(), Boolean.TRUE, delegate.getDelegationType(), "", null);
90                  }
91              }
92          }
93      }
94  
95      protected void generateRequestForRoleMember(Context context, PeopleFlowMember member, String actionRequestPolicyCode) {
96          List<Map<String, String>> roleQualifierList = loadRoleQualifiers(context, member);
97          Role role = getRoleService().getRole(member.getMemberId());
98  
99          if (role == null) {
100             throw new IllegalStateException("Failed to locate a role with the given role id of '" + member.getMemberId() + "'");
101         }
102 
103         if (CollectionUtils.isEmpty(roleQualifierList)) {
104             addKimRoleRequest(context, role, member, Collections.<String, String>emptyMap(), actionRequestPolicyCode );
105         } else {
106             for (Map<String, String> roleQualifiers : roleQualifierList) {
107                 addKimRoleRequest(context, role, member, roleQualifiers, actionRequestPolicyCode );
108             }
109         }
110         // TODO - KULRICE-5726 - still need to implement support for ignoring built-in kim delegates whenever peopleflow delegate(s) are defined
111     }
112 
113 
114     private void addKimRoleRequest(Context context, Role role, PeopleFlowMember member, Map<String, String> roleQualifiers, String actionRequestPolicyCode) {
115         List<RoleMembership> memberships = getRoleService().getRoleMembers(Collections.singletonList(
116                 member.getMemberId()), roleQualifiers);
117 
118         if (!CollectionUtils.isEmpty(memberships)) {
119             context.getActionRequestFactory().addKimRoleRequest(context.getActionRequested().getCode(), member.getPriority(),
120                     role, memberships, null, member.getResponsibilityId(), true, actionRequestPolicyCode, null);
121         }
122     }
123 
124     protected List<Map<String, String>> loadRoleQualifiers(Context context, PeopleFlowMember member) {
125         PeopleFlowTypeService peopleFlowTypeService = context.getPeopleFlowTypeService();
126         List<Map<String, String>> roleQualifierList = new ArrayList<Map<String, String>>();
127 
128         if (peopleFlowTypeService != null) {
129             Document document = DocumentRouteHeaderValue.to(context.getRouteContext().getDocument());
130             DocumentRouteHeaderValueContent content = new DocumentRouteHeaderValueContent(document.getDocumentId());
131             content.setDocumentContent(context.getRouteContext().getDocumentContent().getDocContent());
132             DocumentContent documentContent = DocumentRouteHeaderValueContent.to(content);
133             Map<String, String> roleQualifiers = peopleFlowTypeService.resolveRoleQualifiers(
134                     context.getPeopleFlow().getTypeId(), member.getMemberId(), document, documentContent);
135 
136             if (roleQualifiers != null) {
137                 roleQualifierList.add(roleQualifiers);
138             }
139 
140             boolean versionOk = VersionHelper.compareVersion(context.getPeopleFlowTypeServiceVersion(), CoreConstants.Versions.VERSION_2_3_0)!=-1? true:false;
141             if(versionOk) {
142                 List<Map<String, String>> multipleRoleQualifiers = peopleFlowTypeService.resolveMultipleRoleQualifiers(
143                         context.getPeopleFlow().getTypeId(), member.getMemberId(), document, documentContent);
144 
145                 if (multipleRoleQualifiers != null) {
146                     roleQualifierList.addAll(multipleRoleQualifiers);
147                 }
148             }
149 
150         }
151 
152         return roleQualifierList;
153     }
154 
155     private Recipient toRecipient(PeopleFlowMember member) {
156         Recipient recipient;
157         if (MemberType.PRINCIPAL == member.getMemberType()) {
158             recipient = new KimPrincipalRecipient(member.getMemberId());
159         } else if (MemberType.GROUP == member.getMemberType()) {
160             recipient = new KimGroupRecipient(member.getMemberId());
161         } else {
162             throw new IllegalStateException("encountered a member type which I did not understand: " +
163                     member.getMemberType());
164         }
165         return recipient;
166     }
167 
168     private Recipient toRecipient(PeopleFlowDelegate delegate) {
169         Recipient recipient;
170         if (MemberType.PRINCIPAL == delegate.getMemberType()) {
171             recipient = new KimPrincipalRecipient(delegate.getMemberId());
172         } else if (MemberType.GROUP == delegate.getMemberType()) {
173             recipient = new KimGroupRecipient(delegate.getMemberId());
174         } else {
175             throw new IllegalStateException("encountered a delegate member type which I did not understand: " +
176                     delegate.getMemberType());
177         }
178         return recipient;
179     }
180 
181     public KewTypeRepositoryService getTypeRepositoryService() {
182         return typeRepositoryService;
183     }
184 
185     public void setTypeRepositoryService(KewTypeRepositoryService typeRepositoryService) {
186         this.typeRepositoryService = typeRepositoryService;
187     }
188 
189     public RoleService getRoleService() {
190         return roleService;
191     }
192 
193     public void setRoleService(RoleService roleService) {
194         this.roleService = roleService;
195     }
196 
197     /**
198      * A simple class used to hold context during the PeopleFlow action request generation process.  Construction of
199      * the context will validate that the given values are valid, non-null values where appropriate.
200      */
201     final class Context {
202 
203         private final RouteContext routeContext;
204         private final PeopleFlowDefinition peopleFlow;
205         private final ActionRequestType actionRequested;
206         private final ActionRequestFactory actionRequestFactory;
207 
208         // lazily loaded
209         private PeopleFlowTypeService peopleFlowTypeService;
210         private boolean peopleFlowTypeServiceLoaded = false;
211         private String peopleFlowTypeServiceVersion;
212 
213         Context(RouteContext routeContext, PeopleFlowDefinition peopleFlow, ActionRequestType actionRequested) {
214             if (routeContext == null) {
215                 throw new IllegalArgumentException("routeContext was null");
216             }
217             if (peopleFlow == null) {
218                 throw new IllegalArgumentException("peopleFlow was null");
219             }
220             if (!peopleFlow.isActive()) {
221                 throw new ConfigurationException("Attempted to route to a PeopleFlow that is not active! " + peopleFlow);
222             }
223             if (actionRequested == null) {
224                 actionRequested = ActionRequestType.APPROVE;
225             }
226             this.routeContext = routeContext;
227             this.peopleFlow = peopleFlow;
228             this.actionRequested = actionRequested;
229             this.actionRequestFactory = new ActionRequestFactory(routeContext);
230         }
231 
232         RouteContext getRouteContext() {
233             return routeContext;
234         }
235 
236         PeopleFlowDefinition getPeopleFlow() {
237             return peopleFlow;
238         }
239 
240         ActionRequestType getActionRequested() {
241             return actionRequested;
242         }
243 
244         ActionRequestFactory getActionRequestFactory() {
245             return actionRequestFactory;
246         }
247 
248         /**
249          * Lazily loads and caches the {@code PeopleFlowTypeService} (if necessary) and returns it.
250          */
251         PeopleFlowTypeService getPeopleFlowTypeService() {
252             if (peopleFlowTypeServiceLoaded) {
253                 return this.peopleFlowTypeService;
254             }
255 
256             if (getPeopleFlow().getTypeId() != null) {
257                 KewTypeDefinition typeDefinition = getTypeRepositoryService().getTypeById(getPeopleFlow().getTypeId());
258 
259                 if (typeDefinition == null) {
260                     throw new IllegalStateException("Failed to locate a PeopleFlow type for the given type id of '" + getPeopleFlow().getTypeId() + "'");
261                 }
262 
263                 if (StringUtils.isNotBlank(typeDefinition.getServiceName())) {
264                     Endpoint endpoint = KsbApiServiceLocator.getServiceBus().getEndpoint(QName.valueOf(typeDefinition.getServiceName()));
265 
266                     if (endpoint == null) {
267                         throw new IllegalStateException("Failed to load the PeopleFlowTypeService with the name '" + typeDefinition.getServiceName() + "'");
268                     }
269 
270                     this.peopleFlowTypeService = (PeopleFlowTypeService)endpoint.getService();
271                     this.peopleFlowTypeServiceVersion = endpoint.getServiceConfiguration().getServiceVersion();
272                 }
273             }
274             peopleFlowTypeServiceLoaded = true;
275             return this.peopleFlowTypeService;
276         }
277 
278         String getPeopleFlowTypeServiceVersion() {
279             if (!this.peopleFlowTypeServiceLoaded) {
280                 // execute getPeopleFlowTypeService first to lazy load
281                 getPeopleFlowTypeService();
282             }
283 
284             return this.peopleFlowTypeServiceVersion;
285         }
286     }
287 }