1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.kew.actionrequest.service.impl;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.apache.log4j.Logger;
20 import org.kuali.rice.core.api.config.CoreConfigHelper;
21 import org.kuali.rice.core.api.config.property.ConfigContext;
22 import org.kuali.rice.core.api.criteria.Predicate;
23 import org.kuali.rice.core.api.criteria.QueryByCriteria;
24 import org.kuali.rice.core.api.exception.RiceRuntimeException;
25 import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
26 import org.kuali.rice.kew.actionitem.ActionItem;
27 import org.kuali.rice.kew.actionlist.service.ActionListService;
28 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
29 import org.kuali.rice.kew.actionrequest.Recipient;
30 import org.kuali.rice.kew.actionrequest.dao.ActionRequestDAO;
31 import org.kuali.rice.kew.actionrequest.service.ActionRequestService;
32 import org.kuali.rice.kew.actiontaken.ActionTakenValue;
33 import org.kuali.rice.kew.actiontaken.service.ActionTakenService;
34 import org.kuali.rice.kew.api.KewApiConstants;
35 import org.kuali.rice.kew.api.KewApiServiceLocator;
36 import org.kuali.rice.kew.api.action.ActionRequestPolicy;
37 import org.kuali.rice.kew.api.action.ActionRequestStatus;
38 import org.kuali.rice.kew.api.action.RecipientType;
39 import org.kuali.rice.kew.api.document.DocumentRefreshQueue;
40 import org.kuali.rice.kew.doctype.bo.DocumentType;
41 import org.kuali.rice.kew.engine.ActivationContext;
42 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
43 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
44 import org.kuali.rice.kew.routeheader.service.RouteHeaderService;
45 import org.kuali.rice.kew.routemodule.RouteModule;
46 import org.kuali.rice.kew.service.KEWServiceLocator;
47 import org.kuali.rice.kew.util.FutureRequestDocumentStateManager;
48 import org.kuali.rice.kew.util.PerformanceLogger;
49 import org.kuali.rice.kew.util.ResponsibleParty;
50 import org.kuali.rice.kim.api.group.Group;
51 import org.kuali.rice.kim.api.identity.principal.Principal;
52 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
53 import org.kuali.rice.krad.data.DataObjectService;
54 import org.kuali.rice.krad.data.PersistenceOption;
55 import org.kuali.rice.krad.util.KRADConstants;
56
57 import java.sql.Timestamp;
58 import java.util.ArrayList;
59 import java.util.Collection;
60 import java.util.Collections;
61 import java.util.HashMap;
62 import java.util.HashSet;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.Set;
66
67 import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
68
69
70
71
72
73
74 public class ActionRequestServiceImpl implements ActionRequestService {
75
76 private static final Logger LOG = Logger.getLogger(ActionRequestServiceImpl.class);
77
78 private static final String STATUS = "status";
79 private static final String DOCUMENT_ID = "documentId";
80 private static final String CURRENT_INDICATOR = "currentIndicator";
81 private static final String PARENT_ACTION_REQUEST = "parentActionRequest";
82 private static final String ACTION_REQUESTED = "actionRequested";
83 private static final String ROUTE_NODE_INSTANCE_ID = "nodeInstance.routeNodeInstanceId";
84 private static final String GROUP_ID = "groupId";
85 private static final String ACTION_TAKEN_ID = "actionTaken.actionTakenId";
86 private static final String RECIPIENT_TYPE_CD = "recipientTypeCd";
87 private static final String PRINCIPAL_ID = "principalId";
88
89 private DataObjectService dataObjectService;
90 private ActionRequestDAO actionRequestDAO;
91
92 @Override
93 public ActionRequestValue findByActionRequestId(String actionRequestId) {
94 return getDataObjectService().find(ActionRequestValue.class, actionRequestId);
95 }
96
97 @Override
98 public Map<String, String> getActionsRequested(DocumentRouteHeaderValue routeHeader, String principalId, boolean completeAndApproveTheSame) {
99 return getActionsRequested(principalId, routeHeader.getActionRequests(), completeAndApproveTheSame);
100 }
101
102
103
104
105 protected Map<String, String> getActionsRequested(String principalId, List<ActionRequestValue> actionRequests, boolean completeAndApproveTheSame) {
106 Map<String, String> actionsRequested = new HashMap<String, String>();
107 actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "false");
108 actionsRequested.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, "false");
109 actionsRequested.put(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, "false");
110 actionsRequested.put(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, "false");
111 String topActionRequested = KewApiConstants.ACTION_REQUEST_FYI_REQ;
112 for (ActionRequestValue actionRequest : actionRequests) {
113
114
115
116 if (!RecipientType.ROLE.getCode().equals(actionRequest.getRecipientTypeCd()) &&
117 actionRequest.isRecipientRoutedRequest(principalId) && actionRequest.isActive()) {
118 int actionRequestComparison = ActionRequestValue.compareActionCode(actionRequest.getActionRequested(), topActionRequested, completeAndApproveTheSame);
119 if (actionRequest.isFYIRequest() && actionRequestComparison >= 0) {
120 actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "true");
121 } else if (actionRequest.isAcknowledgeRequest() && actionRequestComparison >= 0) {
122 actionsRequested.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, "true");
123 actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "false");
124 topActionRequested = actionRequest.getActionRequested();
125 } else if (actionRequest.isApproveRequest() && actionRequestComparison >= 0) {
126 actionsRequested.put(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, "true");
127 actionsRequested.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, "false");
128 actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "false");
129 topActionRequested = actionRequest.getActionRequested();
130 } else if (actionRequest.isCompleteRequst() && actionRequestComparison >= 0) {
131 actionsRequested.put(KewApiConstants.ACTION_REQUEST_COMPLETE_REQ, "true");
132 actionsRequested.put(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, "false");
133 actionsRequested.put(KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, "false");
134 actionsRequested.put(KewApiConstants.ACTION_REQUEST_FYI_REQ, "false");
135 if (completeAndApproveTheSame) {
136 actionsRequested.put(KewApiConstants.ACTION_REQUEST_APPROVE_REQ, "true");
137 }
138 topActionRequested = actionRequest.getActionRequested();
139 }
140 }
141 }
142 return actionsRequested;
143 }
144
145 @Override
146 public ActionRequestValue initializeActionRequestGraph(ActionRequestValue actionRequest,
147 DocumentRouteHeaderValue document, RouteNodeInstance nodeInstance) {
148 if (actionRequest.getParentActionRequest() != null) {
149 LOG.warn("-->A non parent action request from doc " + document.getDocumentId());
150 actionRequest = KEWServiceLocator.getActionRequestService().getRoot(actionRequest);
151 }
152 propagatePropertiesToRequestGraph(actionRequest, document, nodeInstance);
153 return actionRequest;
154 }
155
156 private void propagatePropertiesToRequestGraph(ActionRequestValue actionRequest, DocumentRouteHeaderValue document,
157 RouteNodeInstance nodeInstance) {
158 setPropertiesToRequest(actionRequest, document, nodeInstance);
159 for (ActionRequestValue actionRequestValue : actionRequest.getChildrenRequests())
160 {
161 propagatePropertiesToRequestGraph(actionRequestValue, document, nodeInstance);
162 }
163 }
164
165 private void setPropertiesToRequest(ActionRequestValue actionRequest, DocumentRouteHeaderValue document,
166 RouteNodeInstance nodeInstance) {
167 actionRequest.setDocumentId(document.getDocumentId());
168 actionRequest.setDocVersion(document.getDocVersion());
169 actionRequest.setRouteLevel(document.getDocRouteLevel());
170 actionRequest.setNodeInstance(nodeInstance);
171 actionRequest.setStatus(ActionRequestStatus.INITIALIZED.getCode());
172 }
173
174
175
176 @Override
177 public List<ActionRequestValue> activateRequests(List<ActionRequestValue> actionRequests) {
178 return activateRequests(actionRequests, new ActivationContext(!ActivationContext.CONTEXT_IS_SIMULATION));
179 }
180
181 @Override
182 public List<ActionRequestValue> activateRequests(List<ActionRequestValue> actionRequests, boolean simulate) {
183 return activateRequests(actionRequests, new ActivationContext(simulate));
184 }
185
186 @Override
187 public List<ActionRequestValue> activateRequests(List<ActionRequestValue> actionRequests, ActivationContext activationContext) {
188 if (actionRequests == null) {
189 return new ArrayList<ActionRequestValue>();
190 }
191 PerformanceLogger performanceLogger = null;
192 if ( LOG.isInfoEnabled() ) {
193 performanceLogger = new PerformanceLogger();
194 }
195 activationContext.setGeneratedActionItems(new ArrayList<ActionItem>());
196
197
198
199
200 if (!activationContext.isSimulation()) {
201 actionRequests = saveActionRequests(actionRequests);
202 }
203
204 activateRequestsInternal(actionRequests, activationContext);
205 if (!activationContext.isSimulation()) {
206 KEWServiceLocator.getNotificationService().notify(ActionItem.to(activationContext.getGeneratedActionItems()));
207 }
208 if ( LOG.isInfoEnabled() ) {
209 performanceLogger.log("Time to " + (activationContext.isSimulation() ? "simulate activation of " : "activate ")
210 + actionRequests.size() + " action requests.");
211 }
212 if ( LOG.isDebugEnabled() ) {
213 LOG.debug("Generated " + activationContext.getGeneratedActionItems().size() + " action items.");
214 }
215 return actionRequests;
216 }
217
218 @Override
219 public ActionRequestValue activateRequest(ActionRequestValue actionRequest) {
220 return activateRequests(Collections.singletonList(actionRequest), new ActivationContext(!ActivationContext.CONTEXT_IS_SIMULATION)).get(0);
221 }
222
223 @Override
224 public ActionRequestValue activateRequest(ActionRequestValue actionRequest, boolean simulate) {
225 return activateRequests(Collections.singletonList(actionRequest), new ActivationContext(simulate)).get(0);
226 }
227
228 @Override
229 public ActionRequestValue activateRequest(ActionRequestValue actionRequest, ActivationContext activationContext) {
230 return activateRequests(Collections.singletonList(actionRequest), activationContext).get(0);
231 }
232
233 @Override
234 public ActionRequestValue activateRequestNoNotification(ActionRequestValue actionRequest, ActivationContext activationContext) {
235 activationContext.setGeneratedActionItems(new ArrayList<ActionItem>());
236 actionRequest = saveActionRequest(actionRequest, activationContext.isSimulation());
237 activateRequestInternal(actionRequest, activationContext);
238 return actionRequest;
239 }
240
241
242
243
244
245
246
247
248 private void activateRequestsInternal(List<ActionRequestValue> actionRequests, ActivationContext activationContext) {
249 if (actionRequests != null) {
250 for (ActionRequestValue actionRequest : actionRequests) {
251 activateRequestInternal(actionRequest, activationContext);
252 }
253 }
254 }
255
256
257
258
259
260
261
262
263 private void activateRequestInternal(ActionRequestValue actionRequest, ActivationContext activationContext) {
264 PerformanceLogger performanceLogger = null;
265 if ( LOG.isInfoEnabled() ) {
266 performanceLogger = new PerformanceLogger();
267 }
268 if (actionRequest == null || actionRequest.isActive() || actionRequest.isDeactivated()) {
269 return;
270 }
271 processResponsibilityId(actionRequest);
272 if (deactivateOnActionAlreadyTaken(actionRequest, activationContext)) {
273 return;
274 }
275 if (deactivateOnInactiveGroup(actionRequest, activationContext)) {
276 return;
277 }
278 if (deactivateOnEmptyGroup(actionRequest, activationContext)) {
279 return;
280 }
281 actionRequest.setStatus(ActionRequestStatus.ACTIVATED.getCode());
282 if (!activationContext.isSimulation()) {
283 activationContext.getGeneratedActionItems().addAll(generateActionItems(actionRequest, activationContext));
284 }
285 activateRequestsInternal(actionRequest.getChildrenRequests(), activationContext);
286 activateRequestInternal(actionRequest.getParentActionRequest(), activationContext);
287 if ( LOG.isInfoEnabled() ) {
288 if (activationContext.isSimulation()) {
289 performanceLogger.log("Time to simulate activation of request.");
290 } else {
291 performanceLogger.log("Time to activate action request with id " + actionRequest.getActionRequestId());
292 }
293 }
294 }
295
296
297
298
299
300
301 private List<ActionItem> generateActionItems(ActionRequestValue actionRequest, ActivationContext activationContext) {
302 if ( LOG.isDebugEnabled() ) {
303 LOG.debug("generating the action items for request " + actionRequest.getActionRequestId());
304 }
305 List<ActionItem> actionItems = new ArrayList<ActionItem>();
306 if (!actionRequest.isPrimaryDelegator()) {
307 if (actionRequest.isGroupRequest()) {
308 List<String> principalIds = KimApiServiceLocator.getGroupService().getMemberPrincipalIds(actionRequest.getGroupId());
309 actionItems.addAll(createActionItemsForPrincipals(actionRequest, principalIds));
310 } else if (actionRequest.isUserRequest()) {
311 ActionItem actionItem = getActionListService().createActionItemForActionRequest(actionRequest);
312 actionItems.add(actionItem);
313 }
314 }
315 List<ActionItem> actionItemsToReturn = new ArrayList<ActionItem>(actionItems.size());
316 if (!activationContext.isSimulation()) {
317 for (ActionItem actionItem: actionItems) {
318 if ( LOG.isDebugEnabled() ) {
319 LOG.debug("Saving action item: " + actionItems);
320 }
321 actionItem = getActionListService().saveActionItem(actionItem);
322 actionItemsToReturn.add(actionItem);
323 }
324 } else {
325 actionRequest.getSimulatedActionItems().addAll(actionItems);
326 actionItemsToReturn.addAll(actionItems);
327 }
328 return actionItemsToReturn;
329 }
330
331 private List<ActionItem> createActionItemsForPrincipals(ActionRequestValue actionRequest, List<String> principalIds) {
332 List<ActionItem> actionItems = new ArrayList<ActionItem>();
333 for (String principalId: principalIds) {
334
335 ActionItem actionItem = getActionListService().createActionItemForActionRequest(actionRequest);
336 actionItem.setPrincipalId(principalId);
337 actionItem.setRoleName(actionRequest.getQualifiedRoleName());
338
339
340 String ignoreUnknownPrincipalIdsValue = ConfigContext.getCurrentContextConfig().getProperty(KewApiConstants.WORKFLOW_ACTION_IGNORE_UNKOWN_PRINCIPAL_IDS);
341 boolean ignoreUnknownPrincipalIds = Boolean.parseBoolean(ignoreUnknownPrincipalIdsValue);
342
343 if(principalId==null && ignoreUnknownPrincipalIds)
344 {
345 LOG.warn("Ignoring action item with actionRequestID of " + actionRequest.getActionRequestId() + " due to null principalId.");
346 }
347 else
348 {
349 if(principalId==null)
350 {
351 IllegalArgumentException e = new IllegalArgumentException("Exception thrown when trying to add action item with null principalId");
352 LOG.error(e);
353 throw e;
354 }
355 else
356 {
357 actionItems.add(actionItem);
358 }
359 }
360 }
361 return actionItems;
362 }
363
364 private void processResponsibilityId(ActionRequestValue actionRequest) {
365 if (actionRequest.getResolveResponsibility()) {
366 String responsibilityId = actionRequest.getResponsibilityId();
367 try {
368 RouteModule routeModule = KEWServiceLocator.getRouteModuleService().findRouteModule(actionRequest);
369 if (responsibilityId != null && actionRequest.isRouteModuleRequest()) {
370 if ( LOG.isDebugEnabled() ) {
371 LOG.debug("Resolving responsibility id for action request id=" + actionRequest.getActionRequestId()
372 + " and responsibility id=" + actionRequest.getResponsibilityId());
373 }
374 ResponsibleParty responsibleParty = routeModule.resolveResponsibilityId(actionRequest.getResponsibilityId());
375 if (responsibleParty == null) {
376 return;
377 }
378 if (responsibleParty.getPrincipalId() != null) {
379 Principal user = KimApiServiceLocator.getIdentityService()
380 .getPrincipal(responsibleParty.getPrincipalId());
381 actionRequest.setPrincipalId(user.getPrincipalId());
382 } else if (responsibleParty.getGroupId() != null) {
383 actionRequest.setGroupId(responsibleParty.getGroupId());
384 } else if (responsibleParty.getRoleName() != null) {
385 actionRequest.setRoleName(responsibleParty.getRoleName());
386 }
387 }
388 } catch (Exception e) {
389 LOG.error("Exception thrown when trying to resolve responsibility id " + responsibilityId, e);
390 throw new RuntimeException(e);
391 }
392 }
393 }
394
395 protected boolean deactivateOnActionAlreadyTaken(ActionRequestValue actionRequestToActivate,
396 ActivationContext activationContext) {
397
398 FutureRequestDocumentStateManager futureRequestStateMngr = null;
399
400 if (actionRequestToActivate.isGroupRequest()) {
401 futureRequestStateMngr = new FutureRequestDocumentStateManager(actionRequestToActivate.getRouteHeader(), actionRequestToActivate.getGroup());
402 } else if (actionRequestToActivate.isUserRequest()) {
403 futureRequestStateMngr = new FutureRequestDocumentStateManager(actionRequestToActivate.getRouteHeader(), actionRequestToActivate.getPrincipalId());
404 } else {
405 return false;
406 }
407
408 if (futureRequestStateMngr.isReceiveFutureRequests()) {
409 return false;
410 }
411 if (!actionRequestToActivate.getForceAction() || futureRequestStateMngr.isDoNotReceiveFutureRequests()) {
412 ActionTakenValue previousActionTaken = null;
413 if (!activationContext.isSimulation()) {
414 previousActionTaken = getActionTakenService().getPreviousAction(actionRequestToActivate);
415 } else {
416 previousActionTaken = getActionTakenService().getPreviousAction(actionRequestToActivate,
417 activationContext.getSimulatedActionsTaken());
418 }
419 if (previousActionTaken != null) {
420 if ( LOG.isDebugEnabled() ) {
421 LOG.debug("found a satisfying action taken so setting this request done. Action Request Id "
422 + actionRequestToActivate.getActionRequestId());
423 }
424
425
426 if (!previousActionTaken.isForDelegator() && actionRequestToActivate.getParentActionRequest() != null) {
427 previousActionTaken.setDelegator(actionRequestToActivate.getParentActionRequest().getRecipient());
428 if (!activationContext.isSimulation()) {
429 previousActionTaken = getActionTakenService().saveActionTaken(previousActionTaken);
430 }
431 }
432 deactivateRequest(previousActionTaken, actionRequestToActivate, null, activationContext);
433 return true;
434 }
435 }
436 if ( LOG.isDebugEnabled() ) {
437 LOG.debug("Forcing action for action request " + actionRequestToActivate.getActionRequestId());
438 }
439 return false;
440 }
441
442
443
444
445
446
447 protected boolean deactivateOnEmptyGroup(ActionRequestValue actionRequestToActivate, ActivationContext activationContext) {
448 if (actionRequestToActivate.isGroupRequest()) {
449 if (KimApiServiceLocator.getGroupService().getMemberPrincipalIds(actionRequestToActivate.getGroup().getId()).isEmpty()) {
450 deactivateRequest(null, actionRequestToActivate, null, activationContext);
451 return true;
452 }
453 }
454 return false;
455 }
456
457
458
459
460
461 protected boolean deactivateOnInactiveGroup(ActionRequestValue actionRequestToActivate, ActivationContext activationContext) {
462 if (actionRequestToActivate.isGroupRequest()) {
463 if (!actionRequestToActivate.getGroup().isActive() && !actionRequestToActivate.getRouteHeader().getDocumentType().getFailOnInactiveGroup().getPolicyValue()) {
464 deactivateRequest(null, actionRequestToActivate, null, activationContext);
465 return true;
466 }
467 }
468 return false;
469 }
470
471 @Override
472 public ActionRequestValue deactivateRequest(ActionTakenValue actionTaken, ActionRequestValue actionRequest) {
473 return deactivateRequest(actionTaken, actionRequest, null, new ActivationContext(!ActivationContext.CONTEXT_IS_SIMULATION));
474 }
475
476 @Override
477 public ActionRequestValue deactivateRequest(ActionTakenValue actionTaken, ActionRequestValue actionRequest,
478 ActivationContext activationContext) {
479 return deactivateRequest(actionTaken, actionRequest, null, activationContext);
480 }
481
482 @Override
483 public List<ActionRequestValue> deactivateRequests(ActionTakenValue actionTaken, List<ActionRequestValue> actionRequests) {
484 return deactivateRequests(actionTaken, actionRequests, null,
485 new ActivationContext(!ActivationContext.CONTEXT_IS_SIMULATION));
486 }
487
488 @Override
489 public List<ActionRequestValue> deactivateRequests(ActionTakenValue actionTaken, List<ActionRequestValue> actionRequests, boolean simulate) {
490 return deactivateRequests(actionTaken, actionRequests, null, new ActivationContext(simulate));
491 }
492
493 @Override
494 public List<ActionRequestValue> deactivateRequests(ActionTakenValue actionTaken, List<ActionRequestValue> actionRequests, ActivationContext activationContext) {
495 return deactivateRequests(actionTaken, actionRequests, null, activationContext);
496 }
497
498 private List<ActionRequestValue> deactivateRequests(ActionTakenValue actionTaken, List<ActionRequestValue> actionRequests,
499 ActionRequestValue deactivationRequester, ActivationContext activationContext) {
500 List<ActionRequestValue> deactivatedRequests = new ArrayList<ActionRequestValue>();
501 if (actionRequests != null) {
502 for (ActionRequestValue actionRequest : actionRequests) {
503 deactivatedRequests.add(deactivateRequest(actionTaken, actionRequest, deactivationRequester, activationContext));
504 }
505 }
506 return deactivatedRequests;
507 }
508
509 private ActionRequestValue deactivateRequest(ActionTakenValue actionTaken, ActionRequestValue actionRequest,
510 ActionRequestValue deactivationRequester, ActivationContext activationContext) {
511 if (actionRequest == null || actionRequest.isDeactivated()
512 || haltForAllApprove(actionRequest, deactivationRequester)) {
513 return actionRequest;
514 }
515 actionRequest.setStatus(ActionRequestStatus.DONE.getCode());
516 actionRequest.setActionTaken(actionTaken);
517
518 if (!activationContext.isSimulation()) {
519 if (actionTaken != null) {
520
521
522 actionTaken.getActionRequests().add(actionRequest);
523 }
524 actionRequest = getDataObjectService().save(actionRequest);
525 deleteActionItems(actionRequest, true);
526 }
527 actionRequest.setChildrenRequests(deactivateRequests(actionTaken, actionRequest.getChildrenRequests(), actionRequest, activationContext));
528 actionRequest.setParentActionRequest(deactivateRequest(actionTaken, actionRequest.getParentActionRequest(), actionRequest, activationContext));
529 return actionRequest;
530 }
531
532
533
534
535
536
537 private boolean haltForAllApprove(ActionRequestValue actionRequest, ActionRequestValue deactivationRequester) {
538 if (ActionRequestPolicy.ALL.getCode().equals(actionRequest.getApprovePolicy())
539 && actionRequest.hasChild(deactivationRequester)) {
540 for (ActionRequestValue childRequest : actionRequest.getChildrenRequests()) {
541 if (!childRequest.isDeactivated()) {
542 return true;
543 }
544 }
545 }
546 return false;
547 }
548
549 @Override
550 public List<ActionRequestValue> getRootRequests(Collection<ActionRequestValue> actionRequests) {
551 Set<ActionRequestValue> unsavedRequests = new HashSet<ActionRequestValue>();
552 Map<String, ActionRequestValue> requestMap = new HashMap<String, ActionRequestValue>();
553 for (ActionRequestValue actionRequest1 : actionRequests) {
554 ActionRequestValue actionRequest = actionRequest1;
555 ActionRequestValue rootRequest = getRoot(actionRequest);
556 if (rootRequest.getActionRequestId() != null) {
557 requestMap.put(rootRequest.getActionRequestId(), rootRequest);
558 } else {
559 unsavedRequests.add(rootRequest);
560 }
561 }
562 List<ActionRequestValue> requests = new ArrayList<ActionRequestValue>();
563 requests.addAll(requestMap.values());
564 requests.addAll(unsavedRequests);
565 return requests;
566 }
567
568 @Override
569 public ActionRequestValue getRoot(ActionRequestValue actionRequest) {
570 if (actionRequest == null) {
571 return null;
572 }
573 if (actionRequest.getParentActionRequest() != null) {
574 return getRoot(actionRequest.getParentActionRequest());
575 }
576 return actionRequest;
577 }
578
579
580
581
582
583
584 @Override
585 public List<ActionRequestValue> findAllPendingRequests(String documentId) {
586 return findByStatusAndDocId(ActionRequestStatus.ACTIVATED.getCode(), documentId);
587 }
588
589 @Override
590 public List<ActionRequestValue> findAllValidRequests(String principalId, String documentId, String requestCode) {
591 List<ActionRequestValue> pendingArs =
592 findByStatusAndDocumentId(ActionRequestStatus.ACTIVATED.getCode(), documentId);
593 return findAllValidRequests(principalId, pendingArs, requestCode);
594 }
595
596 protected List<ActionRequestValue> findByStatusAndDocumentId(String statusCode, String documentId) {
597 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
598 equal(STATUS, statusCode),
599 equal(DOCUMENT_ID, documentId),
600 equal(CURRENT_INDICATOR, Boolean.TRUE)
601 );
602 return getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
603 }
604
605 @Override
606 public List<ActionRequestValue> findAllValidRequests(String principalId, List<ActionRequestValue> actionRequests, String requestCode) {
607 List<String> arGroups = KimApiServiceLocator.getGroupService().getGroupIdsByPrincipalId(principalId);
608 return filterActionRequestsByCode(actionRequests, principalId, arGroups, requestCode);
609 }
610
611
612
613
614
615
616
617
618
619
620 @Override
621 public List<ActionRequestValue> filterActionRequestsByCode(List<ActionRequestValue> actionRequests, String principalId, List<String> principalGroupIds, String requestCode) {
622 List<ActionRequestValue> filteredActionRequests = new ArrayList<ActionRequestValue>();
623
624 for (ActionRequestValue ar : actionRequests) {
625 if (ActionRequestValue.compareActionCode(ar.getActionRequested(), requestCode, true) > 0) {
626 continue;
627 }
628 if (ar.isUserRequest() && principalId.equals(ar.getPrincipalId())) {
629 filteredActionRequests.add(ar);
630 } else if (ar.isGroupRequest() && principalGroupIds != null && !principalGroupIds.isEmpty()) {
631 for (String groupId : principalGroupIds) {
632 if (groupId.equals(ar.getGroupId())) {
633 filteredActionRequests.add(ar);
634 }
635 }
636 }
637 }
638
639 return filteredActionRequests;
640 }
641
642 @Override
643 public void updateActionRequestsForResponsibilityChange(Set<String> responsibilityIds) {
644 PerformanceLogger performanceLogger = null;
645 if ( LOG.isInfoEnabled() ) {
646 performanceLogger = new PerformanceLogger();
647 }
648 Collection<String> documentsAffected = getRouteHeaderService().findPendingByResponsibilityIds(responsibilityIds);
649 String cacheWaitValue = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString(KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.RULE_DETAIL_TYPE, KewApiConstants.RULE_CACHE_REQUEUE_DELAY);
650 Long cacheWait = KewApiConstants.DEFAULT_CACHE_REQUEUE_WAIT_TIME;
651 if (!org.apache.commons.lang.StringUtils.isEmpty(cacheWaitValue)) {
652 try {
653 cacheWait = Long.valueOf(cacheWaitValue);
654 } catch (NumberFormatException e) {
655 LOG.warn("Cache wait time is not a valid number: " + cacheWaitValue);
656 }
657 }
658 if ( LOG.isInfoEnabled() ) {
659 LOG.info("Scheduling requeue of " + documentsAffected.size() + " documents, affected by " + responsibilityIds.size()
660 + " responsibility changes. Installing a processing wait time of " + cacheWait
661 + " milliseconds to avoid stale rule cache.");
662 }
663 for (String documentId : documentsAffected) {
664
665 String applicationId = null;
666 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByDocumentId(documentId);
667
668 if (documentType != null) {
669 applicationId = documentType.getApplicationId();
670 }
671
672 if (applicationId == null)
673 {
674 applicationId = CoreConfigHelper.getApplicationId();
675 }
676 if(documentType.getRegenerateActionRequestsOnChange().getPolicyValue()) {
677 DocumentRefreshQueue documentRequeuer = KewApiServiceLocator.getDocumentRequeuerService(applicationId,
678 documentId, cacheWait);
679 documentRequeuer.refreshDocument(documentId);
680 }
681 }
682 if ( LOG.isInfoEnabled() ) {
683 performanceLogger.log("Time to updateActionRequestsForResponsibilityChange");
684 }
685 }
686
687 @Override
688 public void deleteActionRequestGraphNoOutbox(ActionRequestValue actionRequest) {
689 deleteActionRequestGraph(actionRequest, false);
690 }
691
692
693
694
695
696 @Override
697 public void deleteActionRequestGraph(ActionRequestValue actionRequest) {
698 deleteActionRequestGraph(actionRequest, true);
699 }
700
701 protected void deleteActionRequestGraph(ActionRequestValue actionRequest, boolean populateOutbox) {
702 if (actionRequest.getParentActionRequest() != null) {
703 throw new IllegalArgumentException("Must delete action request graph from the root, encountered a request with a parent: " + actionRequest);
704 }
705 deleteActionItemsFromGraph(actionRequest, populateOutbox);
706 if (actionRequest.getActionTakenId() != null) {
707 ActionTakenValue actionTaken = getActionTakenService().findByActionTakenId(actionRequest.getActionTakenId());
708 if (actionTaken != null) {
709 getActionTakenService().delete(actionTaken);
710 }
711 }
712
713 getDataObjectService().delete(actionRequest);
714
715 getDataObjectService().flush(ActionRequestValue.class);
716 }
717
718
719
720
721
722 private void deleteActionItems(ActionRequestValue actionRequest, boolean populateOutbox) {
723 List<ActionItem> actionItems = actionRequest.getActionItems();
724 if ( LOG.isDebugEnabled() ) {
725 LOG.debug("deleting " + actionItems.size() + " action items for action request: " + actionRequest);
726 }
727 for (ActionItem actionItem: actionItems) {
728 if ( LOG.isDebugEnabled() ) {
729 LOG.debug("deleting action item: " + actionItem);
730 }
731 if (populateOutbox) {
732 getActionListService().deleteActionItem(actionItem);
733 } else {
734 getActionListService().deleteActionItemNoOutbox(actionItem);
735 }
736 }
737 }
738
739
740
741
742
743
744 private void deleteActionItemsFromGraph(ActionRequestValue actionRequest, boolean populateOutbox) {
745 if (actionRequest.getParentActionRequest() != null) {
746 throw new IllegalArgumentException("Must delete action item from root of action request graph!");
747 }
748 List<ActionItem> actionItemsToDelete = new ArrayList<ActionItem>();
749 accumulateActionItemsFromGraph(actionRequest, actionItemsToDelete);
750 if ( LOG.isDebugEnabled() ) {
751 LOG.debug("deleting " + actionItemsToDelete.size() + " action items for action request graph: " + actionRequest);
752 }
753 for (ActionItem actionItem : actionItemsToDelete) {
754 if ( LOG.isDebugEnabled() ) {
755 LOG.debug("deleting action item: " + actionItem);
756 }
757 if (populateOutbox) {
758 getActionListService().deleteActionItem(actionItem);
759 } else {
760 getActionListService().deleteActionItemNoOutbox(actionItem);
761 }
762 }
763 }
764
765 private void accumulateActionItemsFromGraph(ActionRequestValue actionRequest, List<ActionItem> actionItems) {
766 actionItems.addAll(actionRequest.getActionItems());
767 for (ActionRequestValue childRequest : actionRequest.getChildrenRequests()) {
768 accumulateActionItemsFromGraph(childRequest, actionItems);
769 }
770 }
771
772
773 @Override
774 public List<ActionRequestValue> findByDocumentIdIgnoreCurrentInd(String documentId) {
775 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(equal(DOCUMENT_ID, documentId));
776 return getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
777 }
778
779 @Override
780 public List<ActionRequestValue> findAllActionRequestsByDocumentId(String documentId) {
781 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
782 equal(DOCUMENT_ID, documentId),
783 equal(CURRENT_INDICATOR, Boolean.TRUE)
784 );
785 return getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
786 }
787
788 @Override
789 public List<ActionRequestValue> findAllRootActionRequestsByDocumentId(String documentId) {
790 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
791 equal(DOCUMENT_ID, documentId),
792 equal(CURRENT_INDICATOR, Boolean.TRUE),
793 isNull(PARENT_ACTION_REQUEST)
794 );
795 return getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
796 }
797
798 @Override
799 public List<ActionRequestValue> findPendingByActionRequestedAndDocId(String actionRequestedCd, String documentId) {
800 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
801 equal(DOCUMENT_ID, documentId),
802 equal(CURRENT_INDICATOR, Boolean.TRUE),
803 equal(ACTION_REQUESTED, actionRequestedCd),
804 getPendingCriteria()
805 );
806 return getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
807 }
808
809
810
811 @Override
812 public List<String> getPrincipalIdsWithPendingActionRequestByActionRequestedAndDocId(String actionRequestedCd, String documentId) {
813 List<String> principalIds = new ArrayList<String>();
814 List<ActionRequestValue> actionRequests = findPendingByActionRequestedAndDocId(actionRequestedCd, documentId);
815 for(ActionRequestValue actionRequest: actionRequests){
816 if(actionRequest.isUserRequest()){
817 principalIds.add(actionRequest.getPrincipalId());
818 } else if(actionRequest.isGroupRequest()){
819 principalIds.addAll(
820 KimApiServiceLocator.getGroupService().getMemberPrincipalIds(actionRequest.getGroupId()));
821 }
822 }
823 return principalIds;
824 }
825
826 @Override
827 public List<ActionRequestValue> findPendingRootRequestsByDocId(String documentId) {
828 return getRootRequests(findPendingByDoc(documentId));
829 }
830
831 @Override
832 public List<ActionRequestValue> findPendingRootRequestsByDocIdAtRouteNode(String documentId, String nodeInstanceId) {
833 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
834 equal(DOCUMENT_ID, documentId),
835 equal(CURRENT_INDICATOR, Boolean.TRUE),
836 isNull(PARENT_ACTION_REQUEST),
837 getPendingCriteria(),
838 equal(ROUTE_NODE_INSTANCE_ID, nodeInstanceId)
839 );
840 return getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
841 }
842
843 @Override
844 public List<ActionRequestValue> findRootRequestsByDocIdAtRouteNode(String documentId, String nodeInstanceId) {
845 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
846 equal(DOCUMENT_ID, documentId),
847 equal(CURRENT_INDICATOR, Boolean.TRUE),
848 isNull(PARENT_ACTION_REQUEST),
849 equal(ROUTE_NODE_INSTANCE_ID, nodeInstanceId)
850 );
851 return getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
852 }
853
854 @Override
855 public List<ActionRequestValue> findPendingRootRequestsByDocumentType(String documentTypeId) {
856 return getActionRequestDAO().findPendingRootRequestsByDocumentType(documentTypeId);
857 }
858
859 @Override
860 public ActionRequestValue saveActionRequest(ActionRequestValue actionRequest) {
861 return saveActionRequest(actionRequest, false);
862 }
863
864 protected ActionRequestValue saveActionRequest(ActionRequestValue actionRequest, boolean simulation) {
865 if (actionRequest.isGroupRequest()) {
866 Group group = actionRequest.getGroup();
867 if (group == null) {
868 throw new RiceRuntimeException("Attempted to save an action request with a non-existent group.");
869 }
870 if (!group.isActive() && actionRequest.getRouteHeader().getDocumentType().getFailOnInactiveGroup().getPolicyValue()) {
871 throw new RiceRuntimeException("Attempted to save an action request with an inactive group.");
872 }
873 }
874 if (actionRequest.getActionRequestId() == null) {
875 loadDefaultValues(actionRequest);
876 }
877 if ( actionRequest.getAnnotation() != null && actionRequest.getAnnotation().length() > 2000 ) {
878 actionRequest.setAnnotation( StringUtils.abbreviate(actionRequest.getAnnotation(), 2000) );
879 }
880 if (simulation) {
881 return actionRequest;
882 } else {
883 return getDataObjectService().save(actionRequest);
884 }
885 }
886
887 private void loadDefaultValues(ActionRequestValue actionRequest) {
888 checkNull(actionRequest.getActionRequested(), "action requested");
889 checkNull(actionRequest.getResponsibilityId(), "responsibility ID");
890 checkNull(actionRequest.getRouteLevel(), "route level");
891 checkNull(actionRequest.getDocVersion(), "doc version");
892 if (actionRequest.getForceAction() == null) {
893 actionRequest.setForceAction(Boolean.FALSE);
894 }
895 if (actionRequest.getStatus() == null) {
896 actionRequest.setStatus(ActionRequestStatus.INITIALIZED.getCode());
897 }
898 if (actionRequest.getPriority() == null) {
899 actionRequest.setPriority(KewApiConstants.ACTION_REQUEST_DEFAULT_PRIORITY);
900 }
901 if (actionRequest.getCurrentIndicator() == null) {
902 actionRequest.setCurrentIndicator(true);
903 }
904 actionRequest.setCreateDate(new Timestamp(System.currentTimeMillis()));
905 }
906
907 private void checkNull(Object value, String valueName) throws RuntimeException {
908 if (value == null) {
909 throw new IllegalArgumentException("Null value for " + valueName);
910 }
911 }
912
913 private List<ActionRequestValue> saveActionRequests(Collection<ActionRequestValue> actionRequests) {
914
915 List<ActionRequestValue> savedRequests = new ArrayList<ActionRequestValue>(actionRequests.size());
916 for (ActionRequestValue actionRequest : actionRequests) {
917 savedRequests.add(saveActionRequest(actionRequest));
918 }
919 return savedRequests;
920 }
921
922 @Override
923 public List<ActionRequestValue> findPendingByDoc(String documentId) {
924 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
925 equal(DOCUMENT_ID, documentId),
926 equal(CURRENT_INDICATOR, Boolean.TRUE),
927 getPendingCriteria()
928 );
929 return getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
930 }
931
932 @Override
933 public List<ActionRequestValue> findPendingByDocRequestCdNodeName(String documentId, String requestCode, String nodeName) {
934 List<ActionRequestValue> requests = new ArrayList<ActionRequestValue>();
935 for (ActionRequestValue actionRequest : findPendingByDoc(documentId)) {
936 if (ActionRequestValue.compareActionCode(actionRequest.getActionRequested(), requestCode, true) > 0) {
937 continue;
938 }
939 if (actionRequest.getNodeInstance() != null && actionRequest.getNodeInstance().getName().equals(nodeName)) {
940 requests.add(actionRequest);
941 }
942 }
943 return requests;
944 }
945
946 @Override
947 public List<ActionRequestValue> findActivatedByGroup(String groupId) {
948 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
949 equal(STATUS, ActionRequestStatus.ACTIVATED.getCode()),
950 equal(GROUP_ID, groupId),
951 equal(CURRENT_INDICATOR, Boolean.TRUE),
952 getPendingCriteria()
953 );
954 return getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
955 }
956
957 @Override
958 public List<ActionRequestValue> findByStatusAndDocId(String statusCode, String documentId) {
959 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
960 equal(STATUS, statusCode),
961 equal(DOCUMENT_ID, documentId),
962 equal(CURRENT_INDICATOR, Boolean.TRUE)
963 );
964 return getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
965 }
966
967 @Override
968 public Recipient findDelegator(List<ActionRequestValue> actionRequests) {
969 Recipient delegator = null;
970 String requestCode = KewApiConstants.ACTION_REQUEST_FYI_REQ;
971 for (Object actionRequest1 : actionRequests)
972 {
973 ActionRequestValue actionRequest = (ActionRequestValue) actionRequest1;
974 ActionRequestValue delegatorRequest = findDelegatorRequest(actionRequest);
975 if (delegatorRequest != null)
976 {
977 if (ActionRequestValue.compareActionCode(delegatorRequest.getActionRequested(), requestCode, true) >= 0)
978 {
979 delegator = delegatorRequest.getRecipient();
980 requestCode = delegatorRequest.getActionRequested();
981 }
982 }
983 }
984 return delegator;
985 }
986
987 @Override
988 public ActionRequestValue findDelegatorRequest(ActionRequestValue actionRequest) {
989 ActionRequestValue parentRequest = actionRequest.getParentActionRequest();
990 if (parentRequest != null && !(parentRequest.isUserRequest() || parentRequest.isGroupRequest())) {
991 parentRequest = findDelegatorRequest(parentRequest);
992 }
993 return parentRequest;
994 }
995
996 @Override
997 public List<ActionRequestValue> getDelegateRequests(ActionRequestValue actionRequest) {
998 List<ActionRequestValue> delegateRequests = new ArrayList<ActionRequestValue>();
999 List<ActionRequestValue> requests = getTopLevelRequests(actionRequest);
1000 for (Object request : requests)
1001 {
1002 ActionRequestValue parentActionRequest = (ActionRequestValue) request;
1003 delegateRequests.addAll(parentActionRequest.getChildrenRequests());
1004 }
1005 return delegateRequests;
1006 }
1007
1008 @Override
1009 public List<ActionRequestValue> getTopLevelRequests(ActionRequestValue actionRequest) {
1010 List<ActionRequestValue> topLevelRequests = new ArrayList<ActionRequestValue>();
1011 if (actionRequest.isRoleRequest()) {
1012 topLevelRequests.addAll(actionRequest.getChildrenRequests());
1013 } else {
1014 topLevelRequests.add(actionRequest);
1015 }
1016 return topLevelRequests;
1017 }
1018
1019 @Override
1020 public boolean doesPrincipalHaveRequest(String principalId, String documentId) {
1021 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
1022 equal(PRINCIPAL_ID, principalId),
1023 equal(DOCUMENT_ID, documentId),
1024 equal(RECIPIENT_TYPE_CD, RecipientType.PRINCIPAL.getCode()),
1025 equal(CURRENT_INDICATOR, Boolean.TRUE)
1026 );
1027 int count = getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getTotalRowCount();
1028 if (count > 0) {
1029 return true;
1030 }
1031
1032
1033 List<String> groupIds = getActionRequestDAO().getRequestGroupIds(documentId);
1034 for (String groupId : groupIds) {
1035 if (KimApiServiceLocator.getGroupService().isMemberOfGroup(principalId, groupId)) {
1036 return true;
1037 }
1038 }
1039 return false;
1040 }
1041
1042 @Override
1043 public ActionRequestValue getActionRequestForRole(String actionTakenId) {
1044 QueryByCriteria.Builder criteria = QueryByCriteria.Builder.create().setPredicates(
1045 equal(ACTION_TAKEN_ID, actionTakenId),
1046 equal(CURRENT_INDICATOR, Boolean.TRUE),
1047 equal(RECIPIENT_TYPE_CD, RecipientType.ROLE.getCode()),
1048 isNull(PARENT_ACTION_REQUEST)
1049 );
1050 List<ActionRequestValue> actionTakenRoleRequests =
1051 getDataObjectService().findMatching(ActionRequestValue.class, criteria.build()).getResults();
1052 if (actionTakenRoleRequests.isEmpty()) {
1053 return null;
1054 }
1055 return actionTakenRoleRequests.get(0);
1056 }
1057
1058
1059
1060
1061
1062
1063
1064 protected Predicate getPendingCriteria() {
1065 return or(
1066 equal(STATUS, ActionRequestStatus.ACTIVATED.getCode()),
1067 equal(STATUS, ActionRequestStatus.INITIALIZED.getCode())
1068 );
1069 }
1070
1071 private ActionListService getActionListService() {
1072 return KEWServiceLocator.getActionListService();
1073 }
1074
1075 private ActionTakenService getActionTakenService() {
1076 return KEWServiceLocator.getActionTakenService();
1077 }
1078
1079 private RouteHeaderService getRouteHeaderService() {
1080 return KEWServiceLocator.getService(KEWServiceLocator.DOC_ROUTE_HEADER_SRV);
1081 }
1082
1083 public DataObjectService getDataObjectService() {
1084 return dataObjectService;
1085 }
1086
1087 public void setDataObjectService(DataObjectService dataObjectService) {
1088 this.dataObjectService = dataObjectService;
1089 }
1090
1091 public ActionRequestDAO getActionRequestDAO() {
1092 return actionRequestDAO;
1093 }
1094
1095 public void setActionRequestDAO(ActionRequestDAO actionRequestDAO) {
1096 this.actionRequestDAO = actionRequestDAO;
1097 }
1098
1099 }