001 /**
002 * Copyright 2005-2013 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 */
016 package org.kuali.rice.kew.actionrequest;
017
018 import org.apache.commons.collections.CollectionUtils;
019 import org.apache.commons.lang.StringUtils;
020 import org.apache.log4j.Logger;
021 import org.kuali.rice.core.api.delegation.DelegationType;
022 import org.kuali.rice.core.api.exception.RiceRuntimeException;
023 import org.kuali.rice.core.api.membership.MemberType;
024 import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
025 import org.kuali.rice.kew.actionrequest.service.ActionRequestService;
026 import org.kuali.rice.kew.api.WorkflowRuntimeException;
027 import org.kuali.rice.kew.api.action.ActionRequestPolicy;
028 import org.kuali.rice.kew.api.action.ActionRequestStatus;
029 import org.kuali.rice.kew.api.action.RecipientType;
030 import org.kuali.rice.kew.api.identity.Id;
031 import org.kuali.rice.kew.api.user.UserId;
032 import org.kuali.rice.kew.api.util.CodeTranslator;
033 import org.kuali.rice.kew.engine.RouteContext;
034 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
035 import org.kuali.rice.kew.identity.service.IdentityHelperService;
036 import org.kuali.rice.kew.role.KimRoleRecipient;
037 import org.kuali.rice.kew.role.KimRoleResponsibilityRecipient;
038 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
039 import org.kuali.rice.kew.rule.ResolvedQualifiedRole;
040 import org.kuali.rice.kew.service.KEWServiceLocator;
041 import org.kuali.rice.kew.user.RoleRecipient;
042 import org.kuali.rice.kew.api.KewApiConstants;
043 import org.kuali.rice.kew.util.Utilities;
044 import org.kuali.rice.kew.workgroup.GroupId;
045 import org.kuali.rice.kim.api.common.delegate.DelegateMember;
046 import org.kuali.rice.kim.api.common.delegate.DelegateType;
047 import org.kuali.rice.kim.api.group.Group;
048 import org.kuali.rice.kim.api.group.GroupService;
049 import org.kuali.rice.kim.api.identity.IdentityService;
050 import org.kuali.rice.kim.api.identity.principal.Principal;
051 import org.kuali.rice.kim.api.identity.principal.PrincipalContract;
052 import org.kuali.rice.kim.api.responsibility.ResponsibilityAction;
053 import org.kuali.rice.kim.api.role.Role;
054 import org.kuali.rice.kim.api.role.RoleMembership;
055 import org.kuali.rice.kim.api.role.RoleService;
056 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
057 import org.kuali.rice.krad.util.KRADConstants;
058 import org.kuali.rice.krad.util.ObjectUtils;
059
060 import java.sql.Timestamp;
061 import java.util.ArrayList;
062 import java.util.Collection;
063 import java.util.HashSet;
064 import java.util.Iterator;
065 import java.util.List;
066 import java.util.Map;
067 import java.util.Set;
068
069
070 /**
071 * A factory to aid in creating the ever-so-gnarly ActionRequestValue object.
072 *
073 * @author Kuali Rice Team (rice.collab@kuali.org)
074 */
075 public class ActionRequestFactory {
076
077 private static final Logger LOG = Logger.getLogger(ActionRequestFactory.class);
078
079 private static RoleService roleService;
080 private static IdentityHelperService identityHelperService;
081 private static IdentityService identityService;
082 private static GroupService groupService;
083 private static ActionRequestService actionRequestService;
084
085 private DocumentRouteHeaderValue document;
086 private RouteNodeInstance routeNode;
087 private List<ActionRequestValue> requestGraphs = new ArrayList<ActionRequestValue>();
088
089 public ActionRequestFactory() {
090 }
091
092 public ActionRequestFactory(DocumentRouteHeaderValue document) {
093 this.document = document;
094 }
095
096 public ActionRequestFactory(DocumentRouteHeaderValue document, RouteNodeInstance routeNode) {
097 this.document = document;
098 this.routeNode = routeNode;
099 }
100
101 public ActionRequestFactory(RouteContext routeContext) {
102 this(routeContext.getDocument(), routeContext.getNodeInstance());
103 }
104
105 /**
106 * Constructs ActionRequestValue using default priority and 0 as responsibility
107 *
108 * @param actionRequested
109 * @param recipient
110 * @param description
111 * @param forceAction
112 * @param annotation
113 * @return ActionRequestValue
114 */
115 public ActionRequestValue createActionRequest(String actionRequested, Recipient recipient, String description, Boolean forceAction, String annotation) {
116 return createActionRequest(actionRequested, new Integer(0), recipient, description, KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID, forceAction, annotation);
117 }
118
119 public ActionRequestValue createActionRequest(String actionRequested, Integer priority, Recipient recipient, String description, String responsibilityId, Boolean forceAction, String annotation) {
120 return createActionRequest(actionRequested, priority, recipient, description, responsibilityId, forceAction, null, null, annotation);
121 }
122
123 public ActionRequestValue createActionRequest(String actionRequested, Integer priority, Recipient recipient, String description, String responsibilityId, Boolean forceAction, String approvePolicy, String ruleId, String annotation) {
124 return createActionRequest(actionRequested, priority, recipient, description, responsibilityId, forceAction, approvePolicy, ruleId, annotation, null);
125 }
126
127 public ActionRequestValue createActionRequest(String actionRequested, Integer priority, Recipient recipient, String description, String responsibilityId, Boolean forceAction, String approvePolicy, String ruleId, String annotation, String requestLabel) {
128 ActionRequestValue actionRequest = new ActionRequestValue();
129 actionRequest.setActionRequested(actionRequested);
130 actionRequest.setDocVersion(document.getDocVersion());
131 actionRequest.setPriority(priority);
132 actionRequest.setRouteHeader(document);
133 actionRequest.setDocumentId(document.getDocumentId());
134 actionRequest.setRouteLevel(document.getDocRouteLevel());
135 actionRequest.setNodeInstance(routeNode);
136 actionRequest.setResponsibilityId(responsibilityId);
137 actionRequest.setResponsibilityDesc(description);
138 actionRequest.setApprovePolicy(approvePolicy);
139 actionRequest.setForceAction(forceAction);
140 actionRequest.setRuleBaseValuesId(ruleId);
141 actionRequest.setAnnotation(annotation);
142 actionRequest.setRequestLabel(requestLabel);
143 setDefaultProperties(actionRequest);
144 resolveRecipient(actionRequest, recipient);
145
146 return actionRequest;
147 }
148
149 public ActionRequestValue createBlankActionRequest() {
150 ActionRequestValue request = new ActionRequestValue();
151 request.setRouteHeader(document);
152 if (document != null) {
153 request.setDocumentId(document.getDocumentId());
154 }
155 request.setNodeInstance(routeNode);
156 return request;
157 }
158
159
160 public ActionRequestValue createNotificationRequest(String actionRequestCode, PrincipalContract principal, String reasonActionCode, PrincipalContract reasonActionUser, String responsibilityDesc) {
161 ActionRequestValue request = createActionRequest(actionRequestCode, new KimPrincipalRecipient(principal), responsibilityDesc, Boolean.TRUE, null);
162 String annotation = generateNotificationAnnotation(reasonActionUser, actionRequestCode, reasonActionCode, request);
163 request.setAnnotation(annotation);
164 return request;
165 }
166
167 //unify these 2 methods if possible
168 public List<ActionRequestValue> generateNotifications(List requests, PrincipalContract principal, Recipient delegator,
169 String notificationRequestCode, String actionTakenCode)
170 {
171 String groupName = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(KewApiConstants.KEW_NAMESPACE,
172 KRADConstants.DetailTypes.WORKGROUP_DETAIL_TYPE,
173 KewApiConstants.NOTIFICATION_EXCLUDED_USERS_WORKGROUP_NAME_IND);
174
175
176 Group notifyExclusionWorkgroup = null;
177 if(!StringUtils.isBlank(groupName)){
178 notifyExclusionWorkgroup = getGroupService().getGroupByNamespaceCodeAndName(
179 Utilities.parseGroupNamespaceCode(groupName), Utilities.parseGroupName(groupName));
180 }
181
182
183
184 return generateNotifications(null, getActionRequestService().getRootRequests(requests), principal, delegator, notificationRequestCode, actionTakenCode, notifyExclusionWorkgroup);
185 }
186
187 /**
188 * Generates a notification request for each action request specified, filtering out the specified principal
189 * and delegator, and exclusion workgroup members from notification list
190 * @param parentRequest if non-null, attaches generated notification requests to this parent action request
191 * @param requests list of ActionRequestValues for which to generate corresponding notification requests
192 * @param principal principal to exclude from notifications
193 * @param delegator delegator to exclude from notifications
194 * @param notificationRequestCode the actionrequest code of generated notifications
195 * @param actionTakenCode the actiontaken code to display as the cause of the notification generation
196 * @param notifyExclusionWorkgroup workgroup whose members should not be sent notifications
197 * @return a list of generated notification requests
198 */
199 private List<ActionRequestValue> generateNotifications(ActionRequestValue parentRequest,
200 List requests, PrincipalContract principal, Recipient delegator, String notificationRequestCode,
201 String actionTakenCode, Group notifyExclusionWorkgroup)
202 {
203 List<ActionRequestValue> notificationRequests = new ArrayList<ActionRequestValue>();
204 for (Iterator iter = requests.iterator(); iter.hasNext();)
205 {
206 ActionRequestValue actionRequest = (ActionRequestValue) iter.next();
207 if (!(actionRequest.isRecipientRoutedRequest(principal.getPrincipalId()) || actionRequest.isRecipientRoutedRequest(delegator)))
208 {
209 // skip user requests to system users
210 if ((notifyExclusionWorkgroup != null) &&
211 (isRecipientInGroup(notifyExclusionWorkgroup, actionRequest.getRecipient())))
212 {
213 continue;
214 }
215 ActionRequestValue notificationRequest = createNotificationRequest(actionRequest, principal, notificationRequestCode, actionTakenCode);
216 notificationRequests.add(notificationRequest);
217 if (parentRequest != null)
218 {
219 notificationRequest.setParentActionRequest(parentRequest);
220 parentRequest.getChildrenRequests().add(notificationRequest);
221 }
222 notificationRequests.addAll(generateNotifications(notificationRequest, actionRequest.getChildrenRequests(), principal, delegator, notificationRequestCode, actionTakenCode, notifyExclusionWorkgroup));
223 }
224 }
225 return notificationRequests;
226 }
227
228 private boolean isRecipientInGroup(Group group, Recipient recipient)
229 {
230 boolean isMember = false;
231
232 if(recipient instanceof KimPrincipalRecipient)
233 {
234 String principalId = ((KimPrincipalRecipient) recipient).getPrincipalId();
235 String groupId = group.getId();
236 isMember = getGroupService().isMemberOfGroup(principalId, groupId);
237 }
238 else if (recipient instanceof KimGroupRecipient)
239 {
240 String kimRecipientId = ((KimGroupRecipient) recipient).getGroup().getId();
241 isMember = getGroupService().isGroupMemberOfGroup(kimRecipientId, group.getId() );
242 }
243 return isMember;
244 }
245
246 private ActionRequestValue createNotificationRequest(ActionRequestValue actionRequest, PrincipalContract reasonPrincipal, String notificationRequestCode, String actionTakenCode) {
247
248 String annotation = generateNotificationAnnotation(reasonPrincipal, notificationRequestCode, actionTakenCode, actionRequest);
249 ActionRequestValue request = createActionRequest(notificationRequestCode, actionRequest.getPriority(), actionRequest.getRecipient(), actionRequest.getResponsibilityDesc(), KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID, Boolean.TRUE, annotation);
250
251 request.setDocVersion(actionRequest.getDocVersion());
252 request.setApprovePolicy(actionRequest.getApprovePolicy());
253 request.setRoleName(actionRequest.getRoleName());
254 request.setQualifiedRoleName(actionRequest.getQualifiedRoleName());
255 request.setQualifiedRoleNameLabel(actionRequest.getQualifiedRoleNameLabel());
256 request.setDelegationType(actionRequest.getDelegationType());
257 return request;
258 }
259
260 private void setDefaultProperties(ActionRequestValue actionRequest) {
261 if (actionRequest.getApprovePolicy() == null) {
262 actionRequest.setApprovePolicy(ActionRequestPolicy.FIRST.getCode());
263 }
264 actionRequest.setCreateDate(new Timestamp(System.currentTimeMillis()));
265 actionRequest.setCurrentIndicator(Boolean.TRUE);
266 if (actionRequest.getForceAction() == null) {
267 actionRequest.setForceAction(Boolean.FALSE);
268 }
269 if (routeNode != null) {
270 actionRequest.setNodeInstance(routeNode);
271 }
272 actionRequest.setJrfVerNbr(new Integer(0));
273 actionRequest.setStatus(ActionRequestStatus.INITIALIZED.getCode());
274 actionRequest.setRouteHeader(document);
275 actionRequest.setDocumentId(document.getDocumentId());
276 }
277
278 private static void resolveRecipient(ActionRequestValue actionRequest, Recipient recipient) {
279 if (recipient instanceof KimPrincipalRecipient) {
280 actionRequest.setRecipientTypeCd(RecipientType.PRINCIPAL.getCode());
281 actionRequest.setPrincipalId(((KimPrincipalRecipient)recipient).getPrincipal().getPrincipalId());
282 } else if (recipient instanceof KimGroupRecipient) {
283 KimGroupRecipient kimGroupRecipient = (KimGroupRecipient)recipient;
284 actionRequest.setRecipientTypeCd(RecipientType.GROUP.getCode());
285 actionRequest.setGroupId(kimGroupRecipient.getGroup().getId());
286 } else if (recipient instanceof RoleRecipient){
287 RoleRecipient role = (RoleRecipient)recipient;
288 actionRequest.setRecipientTypeCd(RecipientType.ROLE.getCode());
289 actionRequest.setRoleName(role.getRoleName());
290 actionRequest.setQualifiedRoleName(role.getQualifiedRoleName());
291 ResolvedQualifiedRole qualifiedRole = role.getResolvedQualifiedRole();
292 if (qualifiedRole != null) {
293 actionRequest.setAnnotation(qualifiedRole.getAnnotation() == null ? "" : qualifiedRole.getAnnotation());
294 actionRequest.setQualifiedRoleNameLabel(qualifiedRole.getQualifiedRoleLabel());
295 }
296 Recipient targetRecipient = role.getTarget();
297 if (role.getTarget() != null) {
298 if (targetRecipient instanceof RoleRecipient) {
299 throw new WorkflowRuntimeException("Role Cannot Target a role problem activating request for document " + actionRequest.getDocumentId());
300 }
301 resolveRecipient(actionRequest, role.getTarget());
302 }
303 } else if (recipient instanceof KimRoleResponsibilityRecipient) {
304 KimRoleResponsibilityRecipient roleResponsibilityRecipient = (KimRoleResponsibilityRecipient)recipient;
305 actionRequest.setRecipientTypeCd(RecipientType.ROLE.getCode());
306 actionRequest.setRoleName(roleResponsibilityRecipient.getResponsibilities().get(0).getRoleId());
307 actionRequest.setQualifiedRoleName(
308 roleResponsibilityRecipient.getResponsibilities().get(0).getResponsibilityName());
309 // what about qualified role name label?
310 // actionRequest.setAnnotation(roleRecipient.getResponsibilities().get(0).getResponsibilityName());
311 Recipient targetRecipient = roleResponsibilityRecipient.getTarget();
312 if (targetRecipient != null) {
313 if (targetRecipient instanceof RoleRecipient) {
314 throw new WorkflowRuntimeException("Role Cannot Target a role problem activating request for document " + actionRequest.getDocumentId());
315 }
316 resolveRecipient(actionRequest, roleResponsibilityRecipient.getTarget());
317 }
318 } else if (recipient instanceof KimRoleRecipient) {
319 KimRoleRecipient roleRecipient = (KimRoleRecipient)recipient;
320 actionRequest.setRecipientTypeCd(RecipientType.ROLE.getCode());
321 Role role = roleRecipient.getRole();
322 actionRequest.setRoleName(role.getId());
323 actionRequest.setQualifiedRoleNameLabel(role.getName());
324 Recipient targetRecipient = roleRecipient.getTarget();
325 if (targetRecipient != null) {
326 if (targetRecipient instanceof RoleRecipient) {
327 throw new WorkflowRuntimeException("Role Cannot Target a role problem activating request for document " + actionRequest.getDocumentId());
328 }
329 resolveRecipient(actionRequest, targetRecipient);
330 }
331 }
332 }
333
334 /**
335 * Creates a root Role Request
336 * @param role
337 * @param actionRequested
338 * @param approvePolicy
339 * @param priority
340 * @param responsibilityId
341 * @param forceAction
342 * @param description
343 * @param ruleId
344 * @return the created root role request
345 */
346 public ActionRequestValue addRoleRequest(RoleRecipient role, String actionRequested, String approvePolicy, Integer priority, String responsibilityId, Boolean forceAction, String description, String ruleId) {
347
348 ActionRequestValue requestGraph = createActionRequest(actionRequested, priority, role, description, responsibilityId, forceAction, approvePolicy, ruleId, null);
349 if (role != null && role.getResolvedQualifiedRole() != null && role.getResolvedQualifiedRole().getRecipients() != null) {
350 int legitimateTargets = 0;
351 for (Iterator<Id> iter = role.getResolvedQualifiedRole().getRecipients().iterator(); iter.hasNext();) {
352 Id recipientId = (Id) iter.next();
353 if (recipientId.isEmpty())
354 {
355 throw new WorkflowRuntimeException("Failed to resolve id of type " + recipientId.getClass().getName() + " returned from role '" + role.getRoleName() + "'. Id returned contained a null or empty value.");
356 }
357 if (recipientId instanceof UserId)
358 {
359 Principal principal = getIdentityHelperService().getPrincipal((UserId) recipientId);
360 if(ObjectUtils.isNotNull(principal)) {
361 role.setTarget(new KimPrincipalRecipient(principal));
362 }
363 } else if (recipientId instanceof GroupId)
364 {
365 role.setTarget(new KimGroupRecipient(getIdentityHelperService().getGroup((GroupId) recipientId)));
366 } else
367 {
368 throw new WorkflowRuntimeException("Could not process the given type of id: " + recipientId.getClass());
369 }
370 if (role.getTarget() != null)
371 {
372 legitimateTargets++;
373 ActionRequestValue request = createActionRequest(actionRequested, priority, role, description, responsibilityId, forceAction, null, ruleId, null);
374 request.setParentActionRequest(requestGraph);
375 requestGraph.getChildrenRequests().add(request);
376 }
377 }
378 if (legitimateTargets == 0) {
379 LOG.warn("Role did not yield any legitimate recipients");
380 }
381 } else {
382 LOG.warn("Didn't create action requests for action request description '" + description + "' because of null role or null part of role object graph.");
383 }
384 requestGraphs.add(requestGraph);
385 return requestGraph;
386 }
387
388 /**
389 * Generates an ActionRequest graph for the given KIM Responsibilities. This graph includes any associated delegations.
390 * @param responsibilities
391 * @param approvePolicy
392 */
393 public void addRoleResponsibilityRequest(List<ResponsibilityAction> responsibilities, String approvePolicy) {
394 if (responsibilities == null || responsibilities.isEmpty()) {
395 LOG.warn("Didn't create action requests for action request description because no responsibilities were defined.");
396 return;
397 }
398 // it's assumed the that all in the list have the same action type code, priority number, etc.
399 String actionTypeCode = responsibilities.get(0).getActionTypeCode();
400 Integer priority = responsibilities.get(0).getPriorityNumber();
401 boolean forceAction = responsibilities.get(0).isForceAction();
402 KimRoleResponsibilityRecipient roleResponsibilityRecipient = new KimRoleResponsibilityRecipient(responsibilities);
403
404 // Creation of a parent graph entry for ????
405 ActionRequestValue requestGraph = null;
406 StringBuffer parentAnnotation = null;
407 // set to allow for suppression of duplicate annotations on the parent action request
408 Set<String> uniqueChildAnnotations = null;
409 if ( responsibilities.size() > 1 ) {
410 requestGraph = createActionRequest(
411 actionTypeCode,
412 priority, roleResponsibilityRecipient,
413 "", // description
414 KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID,
415 forceAction,
416 approvePolicy,
417 null, // ruleId
418 null );// annotation
419 requestGraphs.add(requestGraph);
420 parentAnnotation = new StringBuffer();
421 uniqueChildAnnotations = new HashSet<String>( responsibilities.size() );
422 }
423 StringBuffer annotation = new StringBuffer();
424 for (ResponsibilityAction responsibility : responsibilities) {
425 if ( LOG.isDebugEnabled() ) {
426 LOG.debug( "Processing Responsibility for action request: " + responsibility );
427 }
428 // KFSMI-2381 - pull information from KIM to populate annotation
429 annotation.setLength( 0 );
430 Role role = getRoleService().getRole(responsibility.getRoleId());
431 annotation.append( role.getNamespaceCode() ).append( ' ' ).append( role.getName() ).append( ' ' );
432 Map<String, String> qualifier = responsibility.getQualifier();
433 if ( qualifier != null ) {
434 for ( String key : qualifier.keySet() ) {
435 annotation.append( qualifier.get( key ) ).append( ' ' );
436 }
437 }
438 if (responsibility.getPrincipalId() != null) {
439 roleResponsibilityRecipient.setTarget(new KimPrincipalRecipient(responsibility.getPrincipalId()));
440 } else if (responsibility.getGroupId() != null) {
441 roleResponsibilityRecipient.setTarget(new KimGroupRecipient(responsibility.getGroupId()));
442 } else {
443 throw new RiceRuntimeException("Failed to identify a group or principal on the given ResponsibilityResolutionInfo:" + responsibility);
444 }
445 String annotationStr = annotation.toString();
446 ActionRequestValue request = createActionRequest(
447 responsibility.getActionTypeCode(),
448 responsibility.getPriorityNumber(), roleResponsibilityRecipient,
449 responsibility.getParallelRoutingGroupingCode(), // description
450 responsibility.getResponsibilityId(),
451 responsibility.isForceAction(),
452 // If not nested in a parent action request, ensure that the request
453 // is first approve so delegations of this request do not require
454 // ALL_APPROVE as well
455 (responsibilities.size() == 1)?ActionRequestPolicy.FIRST.getCode():approvePolicy,
456 null, // ruleId
457 annotationStr);
458 // if there is only a single request, don't create the nesting structure
459 if ( responsibilities.size() > 1 ) {
460 request.setParentActionRequest(requestGraph);
461 requestGraph.getChildrenRequests().add(request);
462 if ( !uniqueChildAnnotations.contains(annotationStr) ) {
463 parentAnnotation.append( annotationStr ).append( " -- " );
464 uniqueChildAnnotations.add(annotationStr);
465 }
466 } else {
467 requestGraphs.add(request);
468 }
469 generateKimRoleDelegationRequests(responsibility.getDelegates(), request);
470
471 }
472 if ( responsibilities.size() > 1 ) {
473 requestGraph.setAnnotation( StringUtils.chomp( parentAnnotation.toString(), " -- " ) );
474 }
475 }
476
477 private String generateRoleResponsibilityDelegateAnnotation(DelegateMember member, boolean isPrincipal, boolean isGroup, ActionRequestValue parentRequest) {
478 StringBuffer annotation = new StringBuffer( "Delegation of: " );
479 annotation.append( parentRequest.getAnnotation() );
480 annotation.append( " to " );
481 if (isPrincipal) {
482 annotation.append( "principal " );
483 Principal principal = getIdentityService().getPrincipal(member.getMemberId());
484 if ( principal != null ) {
485 annotation.append( principal.getPrincipalName() );
486 } else {
487 annotation.append( member.getMemberId() );
488 }
489 } else if (isGroup) {
490 annotation.append( "group " );
491 Group group = getGroupService().getGroup( member.getMemberId() );
492 if ( group != null ) {
493 annotation.append( group.getNamespaceCode() ).append( '/' ).append( group.getName() );
494 } else {
495 annotation.append( member.getMemberId() );
496 }
497 } else {
498 annotation.append( "?????? '" );
499 annotation.append( member.getMemberId() );
500 annotation.append( "'" );
501 }
502 return annotation.toString();
503 }
504
505 public ActionRequestValue addDelegationRoleRequest(ActionRequestValue parentRequest, String approvePolicy, RoleRecipient role, String responsibilityId, Boolean forceAction, DelegationType delegationType, String description, String ruleId) {
506 Recipient parentRecipient = parentRequest.getRecipient();
507 if (parentRecipient instanceof RoleRecipient) {
508 throw new WorkflowRuntimeException("Cannot delegate on Role Request. It must be a request to a person or workgroup, although that request may be in a role");
509 }
510 if (! relatedToRoot(parentRequest)) {
511 throw new WorkflowRuntimeException("The parent request is not related to any request managed by this factory");
512 }
513 ActionRequestValue delegationRoleRequest = createActionRequest(parentRequest.getActionRequested(), parentRequest.getPriority(), role, description, responsibilityId, forceAction, approvePolicy, ruleId, null);
514 delegationRoleRequest.setDelegationType(delegationType);
515 int count = 0;
516 for (Iterator<Id> iter = role.getResolvedQualifiedRole().getRecipients().iterator(); iter.hasNext(); count++) {
517 //repeat of createRoleRequest code
518 Id recipientId = iter.next();
519 if (recipientId.isEmpty()) {
520 throw new WorkflowRuntimeException("Failed to resolve id of type " + recipientId.getClass().getName() + " returned from role '" + role.getRoleName() + "'. Id returned contained a null or empty value.");
521 }
522 if (recipientId instanceof UserId) {
523 role.setTarget(new KimPrincipalRecipient(getIdentityHelperService().getPrincipal((UserId) recipientId)));
524 } else if (recipientId instanceof GroupId) {
525 role.setTarget(new KimGroupRecipient(getIdentityHelperService().getGroup((GroupId) recipientId)));
526 } else {
527 throw new WorkflowRuntimeException("Could not process the given type of id: " + recipientId.getClass());
528 }
529 ActionRequestValue request = createActionRequest(parentRequest.getActionRequested(), parentRequest.getPriority(), role, description, responsibilityId, forceAction, null, ruleId, null);
530 request.setDelegationType(delegationType);
531 //end repeat
532 request.setParentActionRequest(delegationRoleRequest);
533 delegationRoleRequest.getChildrenRequests().add(request);
534 }
535
536 //put this mini graph in the larger graph
537 if (count > 0) {
538 parentRequest.getChildrenRequests().add(delegationRoleRequest);
539 delegationRoleRequest.setParentActionRequest(parentRequest);
540 }
541
542 return delegationRoleRequest;
543 }
544
545 public ActionRequestValue addDelegationRequest(ActionRequestValue parentRequest, Recipient recipient, String responsibilityId, Boolean forceAction, DelegationType delegationType, String annotation, String ruleId) {
546 if (! relatedToRoot(parentRequest)) {
547 throw new WorkflowRuntimeException("The parent request is not related to any request managed by this factory");
548 }
549 ActionRequestValue delegationRequest = createActionRequest(parentRequest.getActionRequested(), parentRequest.getPriority(), recipient, parentRequest.getResponsibilityDesc(), responsibilityId, forceAction, null, ruleId, annotation);
550 delegationRequest.setDelegationType(delegationType);
551
552 parentRequest.getChildrenRequests().add(delegationRequest);
553 delegationRequest.setParentActionRequest(parentRequest);
554
555 return delegationRequest;
556 }
557
558 //could probably base behavior off of recipient type
559 public ActionRequestValue addRootActionRequest(String actionRequested, Integer priority, Recipient recipient, String description, String responsibilityId, Boolean forceAction, String approvePolicy, String ruleId) {
560 ActionRequestValue requestGraph = createActionRequest(actionRequested, priority, recipient, description, responsibilityId, forceAction, approvePolicy, ruleId, null);
561 requestGraphs.add(requestGraph);
562 return requestGraph;
563 }
564
565 /**
566 * Generates an ActionRequest graph for the given KIM Responsibilities. This graph includes any associated delegations.
567 */
568 public void addKimRoleRequest(String actionRequestedCode, Integer priority, Role role,
569 List<RoleMembership> memberships, String description, String responsibilityId, boolean forceAction,
570 String actionRequestPolicyCode, String requestLabel) {
571 if (CollectionUtils.isEmpty(memberships)) {
572 LOG.warn("Didn't create action requests for action request description because no role members were defined for role id " + role.getId());
573 return;
574 }
575
576 KimRoleRecipient roleRecipient = new KimRoleRecipient(role);
577
578 // Creation of a parent graph entry for ????
579 ActionRequestValue requestGraph = null;
580 if ( memberships.size() > 1 ) {
581 requestGraph = createActionRequest(
582 actionRequestedCode,
583 priority,
584 roleRecipient,
585 "", // description
586 responsibilityId,
587 forceAction,
588 actionRequestPolicyCode,
589 null, // ruleId
590 null );// annotation
591 requestGraphs.add(requestGraph);
592 }
593 for (RoleMembership membership : memberships) {
594 if ( LOG.isDebugEnabled() ) {
595 LOG.debug( "Processing RoleMembership for action request: " + membership );
596 }
597 if (MemberType.PRINCIPAL.equals(membership.getType())) {
598 roleRecipient.setTarget(new KimPrincipalRecipient(membership.getMemberId()));
599 } else if (MemberType.GROUP.equals(membership.getType())) {
600 roleRecipient.setTarget(new KimGroupRecipient(membership.getMemberId()));
601 } else {
602 throw new RiceRuntimeException("Failed to identify a group or principal on the given RoleMembership:" + membership);
603 }
604 ActionRequestValue request = createActionRequest(
605 actionRequestedCode,
606 priority,
607 roleRecipient,
608 "", // description
609 responsibilityId,
610 forceAction,
611 // If not nested in a parent action request, ensure that the request
612 // is first approve so delegations of this request do not require
613 // ALL_APPROVE as well
614 (memberships.size() == 1) ? ActionRequestPolicy.FIRST.getCode() : actionRequestPolicyCode,
615 null, // ruleId
616 null); // annotation
617 // if there is only a single request, don't create the nesting structure
618 if ( memberships.size() > 1 ) {
619 request.setParentActionRequest(requestGraph);
620 requestGraph.getChildrenRequests().add(request);
621 } else {
622 requestGraphs.add(request);
623 }
624 generateKimRoleDelegationRequests(membership.getDelegates(), request);
625 }
626 }
627
628 private void generateKimRoleDelegationRequests(List<DelegateType> delegates, ActionRequestValue parentRequest) {
629 for (DelegateType delegate : delegates) {
630 for (DelegateMember member : delegate.getMembers()) {
631 Recipient recipient;
632 boolean isPrincipal = MemberType.PRINCIPAL.equals(member.getType());
633 boolean isGroup = MemberType.GROUP.equals(member.getType());
634 if (isPrincipal) {
635 recipient = new KimPrincipalRecipient(member.getMemberId());
636 } else if (isGroup) {
637 recipient = new KimGroupRecipient(member.getMemberId());
638 } else {
639 throw new RiceRuntimeException("Invalid DelegateInfo memberTypeCode encountered, was '" + member.getType() + "'");
640 }
641 String delegationAnnotation = generateRoleResponsibilityDelegateAnnotation(member, isPrincipal, isGroup, parentRequest);
642 addDelegationRequest(parentRequest, recipient, delegate.getDelegationId(), parentRequest.getForceAction(), delegate.getDelegationType(), delegationAnnotation, null);
643 }
644 }
645 }
646
647 //return true if requestGraph (root) is in this requests' parents
648 public boolean relatedToRoot(ActionRequestValue request) {
649 int i = 0;
650 while(i < 3) {
651 if (requestGraphs.contains(request)) {
652 return true;
653 } else if (request == null) {
654 return false;
655 }
656 i++;
657 request = request.getParentActionRequest();
658 }
659 return false;
660 }
661
662 public List<ActionRequestValue> getRequestGraphs() {
663 //clean up all the trailing role requests with no children -
664 requestGraphs.removeAll(cleanUpChildren(requestGraphs));
665 return requestGraphs;
666 }
667
668 private Collection<ActionRequestValue> cleanUpChildren(Collection<ActionRequestValue> children) {
669 Collection<ActionRequestValue> requestsToRemove = new ArrayList<ActionRequestValue>();
670 for (ActionRequestValue aChildren : children)
671 {
672
673 if (aChildren.isRoleRequest())
674 {
675 if (aChildren.getChildrenRequests().isEmpty())
676 {
677 requestsToRemove.add(aChildren);
678 } else
679 {
680 Collection<ActionRequestValue> childRequestsToRemove = cleanUpChildren(aChildren.getChildrenRequests());
681 aChildren.getChildrenRequests().removeAll(childRequestsToRemove);
682 }
683 }
684 }
685 return requestsToRemove;
686 }
687
688 private String generateNotificationAnnotation(PrincipalContract principal, String notificationRequestCode, String actionTakenCode, ActionRequestValue request) {
689 String notification = "Action " + CodeTranslator.getActionRequestLabel(notificationRequestCode) + " generated by Workflow because " + principal.getPrincipalName() + " took action "
690 + CodeTranslator.getActionTakenLabel(actionTakenCode);
691 // FIXME: KULRICE-5201 switched rsp_id to a varchar, so the comparison below is no longer valid
692 // if (request.getResponsibilityId() != null && request.getResponsibilityId() != 0) {
693 // TODO: KULRICE-5329 Verify that this code below makes sense and is sufficient
694 if (request.getResponsibilityId() != null && !KewApiConstants.MACHINE_GENERATED_RESPONSIBILITY_ID.equals(request.getResponsibilityId())) {
695 notification += " Responsibility " + request.getResponsibilityId();
696 }
697 if (request.getRuleBaseValuesId() != null) {
698 notification += " Rule Id " + request.getRuleBaseValuesId();
699 }
700 if (request.getAnnotation() != null && request.getAnnotation().length()!=0){
701 notification += " " + request.getAnnotation();
702 }
703 return notification;
704 }
705
706 protected static ActionRequestService getActionRequestService() {
707 if ( actionRequestService == null ) {
708 actionRequestService = KEWServiceLocator.getActionRequestService();
709 }
710 return actionRequestService;
711 }
712
713 /**
714 * @return the roleService
715 */
716 protected static RoleService getRoleService() {
717 if ( roleService == null ) {
718 roleService = KimApiServiceLocator.getRoleService();
719 }
720 return roleService;
721 }
722
723 /**
724 * @return the identityHelperService
725 */
726 protected static IdentityHelperService getIdentityHelperService() {
727 if ( identityHelperService == null ) {
728 identityHelperService = KEWServiceLocator.getIdentityHelperService();
729 }
730 return identityHelperService;
731 }
732
733 /**
734 * @return the identityService
735 */
736 protected static IdentityService getIdentityService() {
737 if ( identityService == null ) {
738 identityService = KimApiServiceLocator.getIdentityService();
739 }
740 return identityService;
741 }
742
743 protected static GroupService getGroupService() {
744 if ( groupService == null ) {
745 groupService = KimApiServiceLocator.getGroupService();
746 }
747 return groupService;
748 }
749 }