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