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