001 /**
002 * Copyright 2005-2011 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.impl.action;
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.exception.RiceIllegalArgumentException;
022 import org.kuali.rice.core.api.exception.RiceRuntimeException;
023 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
024 import org.kuali.rice.core.framework.services.CoreFrameworkServiceLocator;
025 import org.kuali.rice.kew.actionitem.ActionItem;
026 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
027 import org.kuali.rice.kew.actionrequest.KimPrincipalRecipient;
028 import org.kuali.rice.kew.actionrequest.Recipient;
029 import org.kuali.rice.kew.actiontaken.ActionTakenValue;
030 import org.kuali.rice.kew.api.WorkflowRuntimeException;
031 import org.kuali.rice.kew.api.action.ActionRequest;
032 import org.kuali.rice.kew.api.action.ActionRequestType;
033 import org.kuali.rice.kew.api.action.ActionType;
034 import org.kuali.rice.kew.api.action.AdHocRevoke;
035 import org.kuali.rice.kew.api.action.AdHocToGroup;
036 import org.kuali.rice.kew.api.action.AdHocToPrincipal;
037 import org.kuali.rice.kew.api.action.DocumentActionParameters;
038 import org.kuali.rice.kew.api.action.DocumentActionResult;
039 import org.kuali.rice.kew.api.action.InvalidActionTakenException;
040 import org.kuali.rice.kew.api.action.MovePoint;
041 import org.kuali.rice.kew.api.action.RequestedActions;
042 import org.kuali.rice.kew.api.action.ReturnPoint;
043 import org.kuali.rice.kew.api.action.RoutingReportCriteria;
044 import org.kuali.rice.kew.api.action.ValidActions;
045 import org.kuali.rice.kew.api.action.WorkflowDocumentActionsService;
046 import org.kuali.rice.kew.api.doctype.DocumentTypeService;
047 import org.kuali.rice.kew.api.doctype.IllegalDocumentTypeException;
048 import org.kuali.rice.kew.api.document.Document;
049 import org.kuali.rice.kew.api.document.DocumentContentUpdate;
050 import org.kuali.rice.kew.api.document.DocumentDetail;
051 import org.kuali.rice.kew.api.document.DocumentUpdate;
052 import org.kuali.rice.kew.api.document.PropertyDefinition;
053 import org.kuali.rice.kew.api.document.attribute.WorkflowAttributeDefinition;
054 import org.kuali.rice.kew.api.document.attribute.WorkflowAttributeValidationError;
055 import org.kuali.rice.kew.api.exception.WorkflowException;
056 import org.kuali.rice.kew.definition.AttributeDefinition;
057 import org.kuali.rice.kew.doctype.bo.DocumentType;
058 import org.kuali.rice.kew.dto.DTOConverter;
059 import org.kuali.rice.kew.engine.ActivationContext;
060 import org.kuali.rice.kew.engine.node.RouteNode;
061 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
062 import org.kuali.rice.kew.engine.simulation.SimulationCriteria;
063 import org.kuali.rice.kew.engine.simulation.SimulationResults;
064 import org.kuali.rice.kew.engine.simulation.SimulationWorkflowEngine;
065 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
066 import org.kuali.rice.kew.rule.WorkflowRuleAttribute;
067 import org.kuali.rice.kew.rule.WorkflowAttributeXmlValidator;
068 import org.kuali.rice.kew.rule.bo.RuleAttribute;
069 import org.kuali.rice.kew.rule.xmlrouting.GenericXMLRuleAttribute;
070 import org.kuali.rice.kew.service.KEWServiceLocator;
071 import org.kuali.rice.kew.api.KewApiConstants;
072 import org.kuali.rice.kim.api.identity.principal.Principal;
073 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
074 import org.kuali.rice.krad.util.KRADConstants;
075 import org.kuali.rice.krad.util.ObjectUtils;
076
077 import java.util.ArrayList;
078 import java.util.Collections;
079 import java.util.HashMap;
080 import java.util.HashSet;
081 import java.util.List;
082 import java.util.Map;
083 import java.util.Set;
084
085 /**
086 * Reference implementation of the {@link WorkflowDocumentActionsService} api.
087 *
088 * @author Kuali Rice Team (rice.collab@kuali.org)
089 *
090 */
091 public class WorkflowDocumentActionsServiceImpl implements WorkflowDocumentActionsService {
092
093 private static final Logger LOG = Logger.getLogger(WorkflowDocumentActionsServiceImpl.class);
094
095 private DocumentTypeService documentTypeService;
096
097 private static final DocumentActionCallback ACKNOWLEDGE_CALLBACK = new StandardDocumentActionCallback() {
098 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
099 String annotation) throws WorkflowException {
100 return KEWServiceLocator.getWorkflowDocumentService().acknowledgeDocument(principalId, documentBo,
101 annotation);
102 }
103
104 public String getActionName() {
105 return ActionType.ACKNOWLEDGE.getLabel();
106 }
107 };
108
109 private static final DocumentActionCallback APPROVE_CALLBACK = new StandardDocumentActionCallback() {
110 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
111 String annotation) throws WorkflowException {
112 return KEWServiceLocator.getWorkflowDocumentService().approveDocument(principalId, documentBo, annotation);
113 }
114
115 public String getActionName() {
116 return ActionType.APPROVE.getLabel();
117 }
118 };
119
120 private static final DocumentActionCallback CANCEL_CALLBACK = new StandardDocumentActionCallback() {
121 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
122 String annotation) throws WorkflowException {
123 return KEWServiceLocator.getWorkflowDocumentService().cancelDocument(principalId, documentBo, annotation);
124 }
125
126 public String getActionName() {
127 return ActionType.CANCEL.getLabel();
128 }
129 };
130
131 private static final DocumentActionCallback FYI_CALLBACK = new StandardDocumentActionCallback() {
132 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
133 String annotation) throws WorkflowException {
134 return KEWServiceLocator.getWorkflowDocumentService().clearFYIDocument(principalId, documentBo, annotation);
135 }
136
137 public String getActionName() {
138 return ActionType.FYI.getLabel();
139 }
140 };
141
142 private static final DocumentActionCallback COMPLETE_CALLBACK = new StandardDocumentActionCallback() {
143 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
144 String annotation) throws WorkflowException {
145 return KEWServiceLocator.getWorkflowDocumentService().completeDocument(principalId, documentBo, annotation);
146 }
147
148 public String getActionName() {
149 return ActionType.COMPLETE.getLabel();
150 }
151 };
152
153 private static final DocumentActionCallback DISAPPROVE_CALLBACK = new StandardDocumentActionCallback() {
154 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
155 String annotation) throws WorkflowException {
156 return KEWServiceLocator.getWorkflowDocumentService().disapproveDocument(principalId, documentBo,
157 annotation);
158 }
159
160 public String getActionName() {
161 return ActionType.DISAPPROVE.getLabel();
162 }
163 };
164
165 private static final DocumentActionCallback ROUTE_CALLBACK = new StandardDocumentActionCallback() {
166 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
167 String annotation) throws WorkflowException {
168 return KEWServiceLocator.getWorkflowDocumentService().routeDocument(principalId, documentBo, annotation);
169 }
170
171 public String getActionName() {
172 return ActionType.ROUTE.getLabel();
173 }
174 };
175
176 private static final DocumentActionCallback BLANKET_APPROVE_CALLBACK = new StandardDocumentActionCallback() {
177 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
178 String annotation) throws WorkflowException {
179 return KEWServiceLocator.getWorkflowDocumentService().blanketApproval(principalId, documentBo, annotation,
180 new HashSet<String>());
181 }
182
183 public String getActionName() {
184 return ActionType.BLANKET_APPROVE.getLabel();
185 }
186 };
187
188 private static final DocumentActionCallback SAVE_CALLBACK = new StandardDocumentActionCallback() {
189 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
190 String annotation) throws WorkflowException {
191 return KEWServiceLocator.getWorkflowDocumentService().saveDocument(principalId, documentBo, annotation);
192 }
193
194 public String getActionName() {
195 return ActionType.SAVE.getLabel();
196 }
197 };
198
199 private static final DocumentActionCallback PLACE_IN_EXCEPTION_CALLBACK = new StandardDocumentActionCallback() {
200 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
201 String annotation) throws WorkflowException {
202 return KEWServiceLocator.getWorkflowDocumentService().placeInExceptionRouting(principalId, documentBo,
203 annotation);
204 }
205
206 public String getActionName() {
207 return "Place In Exception";
208 }
209 };
210
211 protected DocumentRouteHeaderValue init(DocumentActionParameters parameters) {
212 String documentId = parameters.getDocumentId();
213 String principalId = parameters.getPrincipalId();
214 DocumentUpdate documentUpdate = parameters.getDocumentUpdate();
215 DocumentContentUpdate documentContentUpdate = parameters.getDocumentContentUpdate();
216 incomingParamCheck(documentId, "documentId");
217 incomingParamCheck(principalId, "principalId");
218 if (LOG.isDebugEnabled()) {
219 LOG.debug("Initializing Document from incoming documentId: " + documentId);
220 }
221 KEWServiceLocator.getRouteHeaderService().lockRouteHeader(documentId, true);
222
223 DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
224 if (document == null) {
225 throw new RiceIllegalArgumentException("Failed to locate a document for document id: " + documentId);
226 }
227 boolean modified = false;
228 if (documentUpdate != null) {
229 document.applyDocumentUpdate(documentUpdate);
230 modified = true;
231 }
232 if (documentContentUpdate != null) {
233 String newDocumentContent = DTOConverter.buildUpdatedDocumentContent(document.getDocContent(),
234 documentContentUpdate, document.getDocumentTypeName());
235 document.setDocContent(newDocumentContent);
236 modified = true;
237 }
238
239 if (modified) {
240 KEWServiceLocator.getRouteHeaderService().saveRouteHeader(document);
241
242 /*
243 * Branch data is not persisted when we call saveRouteHeader so we must Explicitly
244 * save the branch. Noticed issue in: KULRICE-4074 when the future action request info,
245 * which is stored in the branch, was not being persisted.
246 *
247 * The call to setRouteHeaderData will ensure that the variable data is in the branch, but we have
248 * to persist the route header before we can save the branch info.
249 *
250 * Placing here to minimize system impact. We should investigate placing this logic into
251 * saveRouteHeader... but at that point we should just turn auto-update = true on the branch relationship
252 *
253 */
254 this.saveRouteNodeInstances(document);
255
256 }
257
258 return document;
259 }
260
261 /**
262 * This method explicitly saves the branch data if it exists in the routeHeaderValue
263 *
264 * @param routeHeader
265 */
266 private void saveRouteNodeInstances(DocumentRouteHeaderValue routeHeader) {
267
268 List<RouteNodeInstance> routeNodes = routeHeader.getInitialRouteNodeInstances();
269 if (routeNodes != null && !routeNodes.isEmpty()) {
270 for (RouteNodeInstance rni : routeNodes) {
271 KEWServiceLocator.getRouteNodeService().save(rni);
272 }
273 }
274
275 }
276
277 @Override
278 public Document create(String documentTypeName,
279 String initiatorPrincipalId, DocumentUpdate documentUpdate,
280 DocumentContentUpdate documentContentUpdate)
281 throws RiceIllegalArgumentException, IllegalDocumentTypeException, InvalidActionTakenException {
282
283 incomingParamCheck(documentTypeName, "documentTypeName");
284 incomingParamCheck(initiatorPrincipalId, "initiatorPrincipalId");
285
286 if (LOG.isDebugEnabled()) {
287 LOG.debug("Create Document [documentTypeName=" + documentTypeName + ", initiatorPrincipalId="
288 + initiatorPrincipalId + "]");
289 }
290
291 String documentTypeId = documentTypeService.getIdByName(documentTypeName);
292 if (documentTypeId == null) {
293 throw new RiceIllegalArgumentException("Failed to locate a document type with the given name: "
294 + documentTypeName);
295 }
296
297 DocumentRouteHeaderValue documentBo = new DocumentRouteHeaderValue();
298 documentBo.setDocumentTypeId(documentTypeId);
299 documentBo.setInitiatorWorkflowId(initiatorPrincipalId);
300 if (documentUpdate != null) {
301 documentBo.setDocTitle(documentUpdate.getTitle());
302 documentBo.setAppDocId(documentUpdate.getApplicationDocumentId());
303 }
304 if (documentContentUpdate != null) {
305 String newDocumentContent = DTOConverter.buildUpdatedDocumentContent(null, documentContentUpdate,
306 documentTypeName);
307 documentBo.setDocContent(newDocumentContent);
308 }
309
310 try {
311 documentBo = KEWServiceLocator.getWorkflowDocumentService()
312 .createDocument(initiatorPrincipalId, documentBo);
313 } catch (WorkflowException e) {
314 // TODO remove this once we stop throwing WorkflowException everywhere!
315 translateException(e);
316 }
317 return DocumentRouteHeaderValue.to(documentBo);
318 }
319
320 @Override
321 public ValidActions determineValidActions(String documentId, String principalId) {
322 incomingParamCheck(documentId, "documentId");
323 incomingParamCheck(principalId, "principalId");
324 DocumentRouteHeaderValue documentBo = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
325 if (documentBo == null) {
326 throw new RiceIllegalArgumentException("Failed to locate a document for document id: " + documentId);
327 }
328 return determineValidActionsInternal(documentBo, principalId);
329 }
330
331 protected ValidActions determineValidActionsInternal(DocumentRouteHeaderValue documentBo, String principalId) {
332 Principal principal = KEWServiceLocator.getIdentityHelperService().getPrincipal(principalId);
333 return KEWServiceLocator.getActionRegistry().getNewValidActions(principal, documentBo);
334 }
335
336 @Override
337 public RequestedActions determineRequestedActions(String documentId, String principalId) {
338 incomingParamCheck(documentId, "documentId");
339 incomingParamCheck(principalId, "principalId");
340 DocumentRouteHeaderValue documentBo = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
341 if (documentBo == null) {
342 throw new RiceIllegalArgumentException("Failed to locate a document for document id: " + documentId);
343 }
344 KEWServiceLocator.getIdentityHelperService().validatePrincipalId(principalId);
345 return determineRequestedActionsInternal(documentBo, principalId);
346 }
347
348 protected RequestedActions determineRequestedActionsInternal(DocumentRouteHeaderValue documentBo, String principalId) {
349 Map<String, String> actionsRequested = KEWServiceLocator.getActionRequestService().getActionsRequested(documentBo,
350 principalId, true);
351 boolean completeRequested = false;
352 boolean approveRequested = false;
353 boolean acknowledgeRequested = false;
354 boolean fyiRequested = false;
355 for (String actionRequestCode : actionsRequested.keySet()) {
356 if (ActionRequestType.FYI.getCode().equals(actionRequestCode)) {
357 fyiRequested = Boolean.parseBoolean(actionsRequested.get(actionRequestCode));
358 } else if (ActionRequestType.ACKNOWLEDGE.getCode().equals(actionRequestCode)) {
359 acknowledgeRequested = Boolean.parseBoolean(actionsRequested.get(actionRequestCode));
360 } else if (ActionRequestType.APPROVE.getCode().equals(actionRequestCode)) {
361 approveRequested = Boolean.parseBoolean(actionsRequested.get(actionRequestCode));
362 } else if (ActionRequestType.COMPLETE.getCode().equals(actionRequestCode)) {
363 completeRequested = Boolean.parseBoolean(actionsRequested.get(actionRequestCode));
364 }
365 }
366 return RequestedActions.create(completeRequested, approveRequested, acknowledgeRequested, fyiRequested);
367 }
368
369 public DocumentDetail executeSimulation(RoutingReportCriteria reportCriteria) {
370 incomingParamCheck(reportCriteria, "reportCriteria");
371 if ( LOG.isDebugEnabled() ) {
372 LOG.debug("Executing routing report [docId=" + reportCriteria.getDocumentId() + ", docTypeName=" + reportCriteria.getDocumentTypeName() + "]");
373 }
374 SimulationCriteria criteria = SimulationCriteria.from(reportCriteria);
375 return DTOConverter.convertDocumentDetailNew(KEWServiceLocator.getRoutingReportService().report(criteria));
376 }
377
378 protected DocumentActionResult constructDocumentActionResult(DocumentRouteHeaderValue documentBo, String principalId) {
379 Document document = DocumentRouteHeaderValue.to(documentBo);
380 ValidActions validActions = determineValidActionsInternal(documentBo, principalId);
381 RequestedActions requestedActions = determineRequestedActionsInternal(documentBo, principalId);
382 return DocumentActionResult.create(document, validActions, requestedActions);
383 }
384
385 @Override
386 public DocumentActionResult acknowledge(DocumentActionParameters parameters) {
387 incomingParamCheck(parameters, "parameters");
388 return executeActionInternal(parameters, ACKNOWLEDGE_CALLBACK);
389 }
390
391 @Override
392 public DocumentActionResult approve(DocumentActionParameters parameters) {
393 incomingParamCheck(parameters, "parameters");
394 return executeActionInternal(parameters, APPROVE_CALLBACK);
395 }
396
397 @Override
398 public DocumentActionResult adHocToPrincipal(DocumentActionParameters parameters,
399 final AdHocToPrincipal adHocToPrincipal) {
400 incomingParamCheck(parameters, "parameters");
401 incomingParamCheck(adHocToPrincipal, "adHocToPrincipal");
402 return executeActionInternal(parameters,
403 new DocumentActionCallback() {
404 @Override
405 public String getLogMessage(String documentId, String principalId, String annotation) {
406 return "AdHoc Route To Principal [principalId=" + principalId +
407 ", docId=" + documentId +
408 ", actionRequest=" + adHocToPrincipal.getActionRequested() +
409 ", nodeName=" + adHocToPrincipal.getNodeName() +
410 ", targetPrincipalId=" + adHocToPrincipal.getTargetPrincipalId() +
411 ", forceAction=" + adHocToPrincipal.isForceAction() +
412 ", annotation=" + annotation +
413 ", requestLabel=" + adHocToPrincipal.getRequestLabel() + "]";
414 }
415
416 @Override
417 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
418 String principalId, String annotation) throws WorkflowException {
419 return KEWServiceLocator.getWorkflowDocumentService().adHocRouteDocumentToPrincipal(
420 principalId,
421 documentBo,
422 adHocToPrincipal.getActionRequested().getCode(),
423 adHocToPrincipal.getNodeName(),
424 adHocToPrincipal.getPriority(),
425 annotation,
426 adHocToPrincipal.getTargetPrincipalId(),
427 adHocToPrincipal.getResponsibilityDescription(),
428 adHocToPrincipal.isForceAction(),
429 adHocToPrincipal.getRequestLabel());
430 }
431 });
432 }
433
434 @Override
435 public DocumentActionResult adHocToGroup(DocumentActionParameters parameters,
436 final AdHocToGroup adHocToGroup) {
437 incomingParamCheck(parameters, "parameters");
438 incomingParamCheck(adHocToGroup, "adHocToGroup");
439 return executeActionInternal(parameters,
440 new DocumentActionCallback() {
441 @Override
442 public String getLogMessage(String documentId, String principalId, String annotation) {
443 return "AdHoc Route To Group [principalId=" + principalId +
444 ", docId=" + documentId +
445 ", actionRequest=" + adHocToGroup.getActionRequested() +
446 ", nodeName=" + adHocToGroup.getNodeName() +
447 ", targetGroupId=" + adHocToGroup.getTargetGroupId() +
448 ", forceAction=" + adHocToGroup.isForceAction() +
449 ", annotation=" + annotation +
450 ", requestLabel=" + adHocToGroup.getRequestLabel() + "]";
451 }
452
453 @Override
454 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
455 String principalId, String annotation) throws WorkflowException {
456 return KEWServiceLocator.getWorkflowDocumentService().adHocRouteDocumentToGroup(principalId,
457 documentBo,
458 adHocToGroup.getActionRequested().getCode(),
459 adHocToGroup.getNodeName(),
460 adHocToGroup.getPriority(),
461 annotation,
462 adHocToGroup.getTargetGroupId(),
463 adHocToGroup.getResponsibilityDescription(),
464 adHocToGroup.isForceAction(),
465 adHocToGroup.getRequestLabel());
466 }
467 });
468 }
469
470 @Override
471 public DocumentActionResult revokeAdHocRequestById(DocumentActionParameters parameters,
472 final String actionRequestId) {
473 incomingParamCheck(parameters, "parameters");
474 incomingParamCheck(actionRequestId, "actionRequestId");
475 return executeActionInternal(parameters,
476 new DocumentActionCallback() {
477 @Override
478 public String getLogMessage(String documentId, String principalId, String annotation) {
479 return "Revoke AdHoc from Principal [principalId=" + principalId +
480 ", documentId=" + documentId +
481 ", annotation=" + annotation +
482 ", actionRequestId=" + actionRequestId + "]";
483 }
484
485 @Override
486 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
487 String principalId, String annotation) throws WorkflowException {
488 return KEWServiceLocator.getWorkflowDocumentService().revokeAdHocRequests(principalId,
489 documentBo, actionRequestId, annotation);
490 }
491 });
492 }
493
494 @Override
495 public DocumentActionResult revokeAdHocRequests(DocumentActionParameters parameters,
496 final AdHocRevoke revoke) {
497 incomingParamCheck(parameters, "parameters");
498 incomingParamCheck(revoke, "revoke");
499 return executeActionInternal(parameters,
500 new DocumentActionCallback() {
501 @Override
502 public String getLogMessage(String documentId, String principalId, String annotation) {
503 return "Revoke AdHoc Requests [principalId=" + principalId +
504 ", docId=" + documentId +
505 ", annotation=" + annotation +
506 ", revoke=" + revoke.toString() + "]";
507 }
508
509 @Override
510 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
511 String principalId, String annotation) throws WorkflowException {
512 return KEWServiceLocator.getWorkflowDocumentService().revokeAdHocRequests(principalId,
513 documentBo, revoke, annotation);
514 }
515 });
516 }
517
518 @Override
519 public DocumentActionResult revokeAllAdHocRequests(DocumentActionParameters parameters) {
520 incomingParamCheck(parameters, "parameters");
521 return executeActionInternal(parameters,
522 new DocumentActionCallback() {
523 @Override
524 public String getLogMessage(String documentId, String principalId, String annotation) {
525 return "Revoke All AdHoc Requests [principalId=" + principalId +
526 ", docId=" + documentId +
527 ", annotation=" + annotation + "]";
528 }
529
530 @Override
531 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
532 String principalId, String annotation) throws WorkflowException {
533 return KEWServiceLocator.getWorkflowDocumentService().revokeAdHocRequests(principalId,
534 documentBo, (AdHocRevoke) null, annotation);
535 }
536 });
537 }
538
539 @Override
540 public DocumentActionResult cancel(DocumentActionParameters parameters) {
541 incomingParamCheck(parameters, "parameters");
542 return executeActionInternal(parameters, CANCEL_CALLBACK);
543 }
544
545 @Override
546 public DocumentActionResult clearFyi(DocumentActionParameters parameters) {
547 incomingParamCheck(parameters, "parameters");
548 return executeActionInternal(parameters, FYI_CALLBACK);
549 }
550
551 @Override
552 public DocumentActionResult complete(DocumentActionParameters parameters) {
553 incomingParamCheck(parameters, "parameters");
554 return executeActionInternal(parameters, COMPLETE_CALLBACK);
555 }
556
557 @Override
558 public DocumentActionResult disapprove(DocumentActionParameters parameters) {
559 incomingParamCheck(parameters, "parameters");
560 return executeActionInternal(parameters, DISAPPROVE_CALLBACK);
561 }
562
563 @Override
564 public DocumentActionResult route(DocumentActionParameters parameters) {
565 incomingParamCheck(parameters, "parameters");
566 return executeActionInternal(parameters, ROUTE_CALLBACK);
567 }
568
569 @Override
570 public DocumentActionResult blanketApprove(DocumentActionParameters parameters) {
571 incomingParamCheck(parameters, "parameters");
572 return executeActionInternal(parameters, BLANKET_APPROVE_CALLBACK);
573 }
574
575 @Override
576 public DocumentActionResult blanketApproveToNodes(DocumentActionParameters parameters,
577 final Set<String> nodeNames) {
578 incomingParamCheck(parameters, "parameters");
579 incomingParamCheck(nodeNames, "nodeNames");
580 return executeActionInternal(parameters,
581 new DocumentActionCallback() {
582 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
583 String principalId, String annotation) throws WorkflowException {
584 return KEWServiceLocator.getWorkflowDocumentService().blanketApproval(principalId, documentBo,
585 annotation, nodeNames);
586 }
587
588 public String getLogMessage(String documentId, String principalId, String annotation) {
589 return "Blanket Approve [principalId=" + principalId + ", documentId=" + documentId
590 + ", annotation=" + annotation + ", nodeNames=" + nodeNames + "]";
591 }
592 });
593 }
594
595 @Override
596 public DocumentActionResult returnToPreviousNode(DocumentActionParameters parameters,
597 final ReturnPoint returnPoint) {
598 incomingParamCheck(parameters, "parameters");
599 incomingParamCheck(returnPoint, "returnPoint");
600 return executeActionInternal(parameters,
601 new DocumentActionCallback() {
602 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
603 String principalId, String annotation) throws WorkflowException {
604 return KEWServiceLocator.getWorkflowDocumentService().returnDocumentToPreviousNode(principalId,
605 documentBo, returnPoint.getNodeName(), annotation);
606 }
607
608 public String getLogMessage(String documentId, String principalId, String annotation) {
609 return "Return to Previous [principalId=" + principalId + ", documentId=" + documentId
610 + ", annotation=" + annotation + ", destNodeName=" + returnPoint.getNodeName() + "]";
611 }
612 });
613 }
614
615 @Override
616 public DocumentActionResult move(DocumentActionParameters parameters,
617 final MovePoint movePoint) {
618 incomingParamCheck(parameters, "parameters");
619 incomingParamCheck(movePoint, "movePoint");
620 return executeActionInternal(parameters,
621 new DocumentActionCallback() {
622 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
623 String principalId, String annotation) throws WorkflowException {
624 return KEWServiceLocator.getWorkflowDocumentService().moveDocument(principalId, documentBo,
625 movePoint, annotation);
626 }
627
628 public String getLogMessage(String documentId, String principalId, String annotation) {
629 return "Move Document [principalId=" + principalId + ", documentId=" + documentId
630 + ", annotation=" + annotation + ", movePoint=" + movePoint + "]";
631 }
632 });
633 }
634
635 @Override
636 public DocumentActionResult takeGroupAuthority(DocumentActionParameters parameters,
637 final String groupId) {
638 incomingParamCheck(parameters, "parameters");
639 incomingParamCheck(groupId, "groupId");
640 return executeActionInternal(parameters,
641 new StandardDocumentActionCallback() {
642 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
643 String principalId, String annotation) throws WorkflowException {
644 return KEWServiceLocator.getWorkflowDocumentService().takeGroupAuthority(principalId,
645 documentBo, groupId, annotation);
646 }
647
648 public String getActionName() {
649 return ActionType.TAKE_GROUP_AUTHORITY.getLabel();
650 }
651 });
652 }
653
654 @Override
655 public DocumentActionResult releaseGroupAuthority(DocumentActionParameters parameters,
656 final String groupId) {
657 incomingParamCheck(parameters, "parameters");
658 incomingParamCheck(groupId, "groupId");
659 return executeActionInternal(parameters,
660 new StandardDocumentActionCallback() {
661 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
662 String principalId, String annotation) throws WorkflowException {
663 return KEWServiceLocator.getWorkflowDocumentService().releaseGroupAuthority(principalId,
664 documentBo, groupId, annotation);
665 }
666
667 public String getActionName() {
668 return ActionType.RELEASE_GROUP_AUTHORITY.getLabel();
669 }
670 });
671
672 }
673
674 @Override
675 public DocumentActionResult save(DocumentActionParameters parameters) {
676 incomingParamCheck(parameters, "parameters");
677 return executeActionInternal(parameters, SAVE_CALLBACK);
678 }
679
680 @Override
681 public DocumentActionResult saveDocumentData(DocumentActionParameters parameters) {
682 incomingParamCheck(parameters, "parameters");
683 return executeActionInternal(parameters, new DocumentActionCallback() {
684
685 @Override
686 public String getLogMessage(String documentId, String principalId, String annotation) {
687 return "Saving Routing Data [principalId=" + principalId + ", docId=" + documentId + "]";
688 }
689
690 @Override
691 public DocumentRouteHeaderValue doInDocumentBo(
692 DocumentRouteHeaderValue documentBo, String principalId,
693 String annotation) throws WorkflowException {
694 return KEWServiceLocator.getWorkflowDocumentService().saveRoutingData(principalId, documentBo);
695 }
696 });
697 }
698
699 @Override
700 public Document delete(String documentId, String principalId) {
701 incomingParamCheck(documentId, "documentId");
702 incomingParamCheck(principalId, "principalId");
703 DocumentRouteHeaderValue documentBo = init(DocumentActionParameters.create(documentId, principalId, null));
704 if (LOG.isDebugEnabled()) {
705 LOG.debug("Delete [principalId=" + principalId + ", documentId=" + documentId + "]");
706 }
707 Document document = null;
708 try {
709 document = DocumentRouteHeaderValue.to(documentBo);
710 KEWServiceLocator.getWorkflowDocumentService().deleteDocument(principalId, documentBo);
711
712 } catch (WorkflowException e) {
713 translateException(e);
714 }
715 return document;
716 }
717
718 @Override
719 public void logAnnotation(String documentId, String principalId, String annotation) {
720 incomingParamCheck(documentId, "documentId");
721 incomingParamCheck(principalId, "principalId");
722 incomingParamCheck(annotation, "annotation");
723 DocumentRouteHeaderValue documentBo = KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
724 try {
725 KEWServiceLocator.getWorkflowDocumentService().logDocumentAction(principalId, documentBo, annotation);
726 } catch (WorkflowException e) {
727 translateException(e);
728 }
729 }
730
731 @Override
732 public void initiateIndexing(String documentId) {
733 incomingParamCheck(documentId, "documentId");
734 // TODO ewestfal - THIS METHOD NEEDS JAVADOCS
735 throw new UnsupportedOperationException("implement me!!!");
736 }
737
738 @Override
739 public DocumentActionResult superUserBlanketApprove(DocumentActionParameters parameters,
740 final boolean executePostProcessor) {
741 incomingParamCheck(parameters, "parameters");
742 return executeActionInternal(parameters,
743 new DocumentActionCallback() {
744 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
745 String principalId, String annotation) throws WorkflowException {
746 return KEWServiceLocator.getWorkflowDocumentService().superUserApprove(principalId, documentBo,
747 annotation, executePostProcessor);
748 }
749
750 public String getLogMessage(String documentId, String principalId, String annotation) {
751 return "SU Blanket Approve [principalId=" + principalId + ", documentId=" + documentId
752 + ", annotation=" + annotation + "]";
753 }
754 });
755 }
756
757 @Override
758 public DocumentActionResult superUserNodeApprove(DocumentActionParameters parameters,
759 final boolean executePostProcessor, final String nodeName) {
760 incomingParamCheck(parameters, "parameters");
761 incomingParamCheck(nodeName, "nodeName");
762 return executeActionInternal(parameters,
763 new DocumentActionCallback() {
764 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
765 String principalId, String annotation) throws WorkflowException {
766 return KEWServiceLocator.getWorkflowDocumentService().superUserNodeApproveAction(principalId,
767 documentBo, nodeName, annotation, executePostProcessor);
768 }
769
770 public String getLogMessage(String documentId, String principalId, String annotation) {
771 return "SU Node Approve Action [principalId=" + principalId + ", documentId=" + documentId
772 + ", nodeName=" + nodeName + ", annotation=" + annotation + "]";
773 }
774 });
775
776 }
777
778 @Override
779 public DocumentActionResult superUserTakeRequestedAction(DocumentActionParameters parameters,
780 final boolean executePostProcessor, final String actionRequestId) {
781 incomingParamCheck(parameters, "parameters");
782 incomingParamCheck(actionRequestId, "actionRequestId");
783 return executeActionInternal(parameters,
784 new DocumentActionCallback() {
785 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
786 String principalId, String annotation) throws WorkflowException {
787 return KEWServiceLocator.getWorkflowDocumentService().superUserActionRequestApproveAction(
788 principalId, documentBo, actionRequestId, annotation,
789 executePostProcessor);
790 }
791
792 public String getLogMessage(String documentId, String principalId, String annotation) {
793 return "SU Take Requested Action [principalId=" + principalId + ", docume tId=" + documentId
794 + ", actionRequestId=" + actionRequestId + ", annotation=" + annotation + "]";
795 }
796 });
797 }
798
799 @Override
800 public DocumentActionResult superUserDisapprove(DocumentActionParameters parameters,
801 final boolean executePostProcessor) {
802 incomingParamCheck(parameters, "parameters");
803 return executeActionInternal(parameters,
804 new DocumentActionCallback() {
805 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
806 String principalId, String annotation) throws WorkflowException {
807 return KEWServiceLocator.getWorkflowDocumentService().superUserDisapproveAction(principalId,
808 documentBo, annotation, executePostProcessor);
809 }
810
811 public String getLogMessage(String documentId, String principalId, String annotation) {
812 return "SU Disapprove [principalId=" + principalId + ", documentId=" + documentId
813 + ", annotation=" + annotation + "]";
814 }
815 });
816 }
817
818 @Override
819 public DocumentActionResult superUserCancel(DocumentActionParameters parameters, final boolean executePostProcessor) {
820 incomingParamCheck(parameters, "parameters");
821 return executeActionInternal(parameters,
822 new DocumentActionCallback() {
823 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
824 String principalId, String annotation) throws WorkflowException {
825 return KEWServiceLocator.getWorkflowDocumentService().superUserCancelAction(principalId,
826 documentBo, annotation, executePostProcessor);
827 }
828
829 public String getLogMessage(String documentId, String principalId, String annotation) {
830 return "SU Cancel [principalId=" + principalId + ", documentId=" + documentId + ", annotation="
831 + annotation + "]";
832 }
833 });
834 }
835
836 @Override
837 public DocumentActionResult superUserReturnToPreviousNode(DocumentActionParameters parameters,
838 final boolean executePostProcessor, final ReturnPoint returnPoint) {
839 incomingParamCheck(parameters, "parameters");
840 incomingParamCheck(returnPoint, "returnPoint");
841 return executeActionInternal(parameters,
842 new DocumentActionCallback() {
843 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo,
844 String principalId, String annotation) throws WorkflowException {
845 return KEWServiceLocator.getWorkflowDocumentService().superUserReturnDocumentToPreviousNode(
846 principalId, documentBo, returnPoint.getNodeName(), annotation, executePostProcessor);
847 }
848
849 public String getLogMessage(String documentId, String principalId, String annotation) {
850 return "SU Return to Previous Node [principalId=" + principalId + ", documentId=" + documentId
851 + ", annotation=" + annotation + ", returnPoint=" + returnPoint + "]";
852 }
853 });
854
855 }
856
857 @Override
858 public DocumentActionResult placeInExceptionRouting(DocumentActionParameters parameters) {
859 incomingParamCheck(parameters, "parameters");
860 return executeActionInternal(parameters, PLACE_IN_EXCEPTION_CALLBACK);
861 }
862
863 @Override
864 public boolean documentWillHaveAtLeastOneActionRequest(RoutingReportCriteria reportCriteria, List<String> actionRequestedCodes, boolean ignoreCurrentActionRequests) {
865 incomingParamCheck(reportCriteria, "reportCriteria");
866 incomingParamCheck(actionRequestedCodes, "actionRequestedCodes");
867 try {
868 SimulationWorkflowEngine simulationEngine = KEWServiceLocator.getSimulationEngine();
869 SimulationCriteria criteria = SimulationCriteria.from(reportCriteria);
870 // set activate requests to true by default so force action works correctly
871 criteria.setActivateRequests(Boolean.TRUE);
872 SimulationResults results = simulationEngine.runSimulation(criteria);
873 List<ActionRequestValue> actionRequestsToProcess = results.getSimulatedActionRequests();
874 if (!ignoreCurrentActionRequests) {
875 actionRequestsToProcess.addAll(results.getDocument().getActionRequests());
876 }
877 for (ActionRequestValue actionRequest : actionRequestsToProcess) {
878 if (actionRequest.isDone()) {
879 // an action taken has eliminated this request from being active
880 continue;
881 }
882 // if no action request codes are passed in.... assume any request found is
883 if (CollectionUtils.isEmpty(actionRequestedCodes) ) {
884 // we found an action request
885 return true;
886 }
887 // check the action requested codes passed in
888 for (String requestedActionRequestCode : actionRequestedCodes) {
889 if (requestedActionRequestCode.equals(actionRequest.getActionRequested())) {
890 boolean satisfiesDestinationUserCriteria = (criteria.getDestinationRecipients().isEmpty()) || (isRecipientRoutedRequest(actionRequest,criteria.getDestinationRecipients()));
891 if (satisfiesDestinationUserCriteria) {
892 if (StringUtils.isBlank(criteria.getDestinationNodeName())) {
893 return true;
894 } else if (StringUtils.equals(criteria.getDestinationNodeName(),actionRequest.getNodeInstance().getName())) {
895 return true;
896 }
897 }
898 }
899 }
900 }
901 return false;
902 } catch (Exception ex) {
903 String error = "Problems evaluating documentWillHaveAtLeastOneActionRequest: " + ex.getMessage();
904 LOG.error(error,ex);
905 if (ex instanceof RuntimeException) {
906 throw (RuntimeException)ex;
907 }
908 throw new RuntimeException(error, ex);
909 }
910 }
911
912 private boolean isRecipientRoutedRequest(ActionRequestValue actionRequest, List<Recipient> recipients) throws WorkflowException {
913 for (Recipient recipient : recipients) {
914 if (actionRequest.isRecipientRoutedRequest(recipient)) {
915 return true;
916 }
917 }
918 return false;
919 }
920
921 @Override
922 public void reResolveRoleByDocTypeName(String documentTypeName, String roleName, String qualifiedRoleNameLabel) {
923 incomingParamCheck(documentTypeName, "documentTypeName");
924 incomingParamCheck(roleName, "roleName");
925 incomingParamCheck(qualifiedRoleNameLabel, "qualifiedRoleNameLabel");
926 if ( LOG.isDebugEnabled() ) {
927 LOG.debug("Re-resolving Role [docTypeName=" + documentTypeName + ", roleName=" + roleName + ", qualifiedRoleNameLabel=" + qualifiedRoleNameLabel + "]");
928 }
929 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName);
930 if (org.apache.commons.lang.StringUtils.isEmpty(qualifiedRoleNameLabel)) {
931 KEWServiceLocator.getRoleService().reResolveRole(documentType, roleName);
932 } else {
933 KEWServiceLocator.getRoleService().reResolveQualifiedRole(documentType, roleName, qualifiedRoleNameLabel);
934 }
935 }
936
937 public void reResolveRoleByDocumentId(String documentId, String roleName, String qualifiedRoleNameLabel) {
938 incomingParamCheck(documentId, "documentId");
939 incomingParamCheck(roleName, "roleName");
940 incomingParamCheck(qualifiedRoleNameLabel, "qualifiedRoleNameLabel");
941 if ( LOG.isDebugEnabled() ) {
942 LOG.debug("Re-resolving Role [documentId=" + documentId + ", roleName=" + roleName + ", qualifiedRoleNameLabel=" + qualifiedRoleNameLabel + "]");
943 }
944 DocumentRouteHeaderValue routeHeader = loadDocument(documentId);
945 if (org.apache.commons.lang.StringUtils.isEmpty(qualifiedRoleNameLabel)) {
946 KEWServiceLocator.getRoleService().reResolveRole(routeHeader, roleName);
947 } else {
948 KEWServiceLocator.getRoleService().reResolveQualifiedRole(routeHeader, roleName, qualifiedRoleNameLabel);
949 }
950 }
951
952 @Override
953 public List<WorkflowAttributeValidationError> validateWorkflowAttributeDefinition(
954 WorkflowAttributeDefinition definition) {
955 if (definition == null) {
956 throw new RiceIllegalArgumentException("definition was null");
957 }
958 if ( LOG.isDebugEnabled() ) {
959 LOG.debug("Validating WorkflowAttributeDefinition [attributeName="+definition.getAttributeName()+"]");
960 }
961 AttributeDefinition attributeDefinition = DTOConverter.convertWorkflowAttributeDefinition(definition);
962 WorkflowRuleAttribute attribute = null;
963 if (attributeDefinition != null) {
964 attribute = (WorkflowRuleAttribute) GlobalResourceLoader.getObject(attributeDefinition.getObjectDefinition());
965 }
966 if (attribute instanceof GenericXMLRuleAttribute) {
967 Map<String, String> attributePropMap = new HashMap<String, String>();
968 GenericXMLRuleAttribute xmlAttribute = (GenericXMLRuleAttribute)attribute;
969 xmlAttribute.setExtensionDefinition(RuleAttribute.to(attributeDefinition.getRuleAttribute()));
970 for (PropertyDefinition propertyDefinition : definition.getPropertyDefinitions()) {
971 attributePropMap.put(propertyDefinition.getName(), propertyDefinition.getValue());
972 }
973 xmlAttribute.setParamMap(attributePropMap);
974 }
975 List<WorkflowAttributeValidationError> errors = new ArrayList<WorkflowAttributeValidationError>();
976 //validate inputs from client application if the attribute is capable
977 if (attribute instanceof WorkflowAttributeXmlValidator) {
978 List<org.kuali.rice.kew.rule.WorkflowAttributeValidationError> validationErrors = ((WorkflowAttributeXmlValidator)attribute).validateClientRoutingData();
979 if (validationErrors != null) {
980 for (org.kuali.rice.kew.rule.WorkflowAttributeValidationError validationError : validationErrors) {
981 errors.add(org.kuali.rice.kew.rule.WorkflowAttributeValidationError.to(validationError));
982 }
983 }
984 }
985 return errors;
986 }
987
988 @Override
989 public boolean isFinalApprover(String documentId, String principalId) {
990 incomingParamCheck(documentId, "documentId");
991 incomingParamCheck(principalId, "principalId");
992 if ( LOG.isDebugEnabled() ) {
993 LOG.debug("Evaluating isFinalApprover [docId=" + documentId + ", principalId=" + principalId + "]");
994 }
995 DocumentRouteHeaderValue routeHeader = loadDocument(documentId);
996 List<ActionRequestValue> requests = KEWServiceLocator.getActionRequestService().findPendingByDoc(documentId);
997 List<RouteNode> finalApproverNodes = KEWServiceLocator.getRouteNodeService().findFinalApprovalRouteNodes(routeHeader.getDocumentType().getDocumentTypeId());
998 if (finalApproverNodes.isEmpty()) {
999 if ( LOG.isDebugEnabled() ) {
1000 LOG.debug("Could not locate final approval nodes for document " + documentId);
1001 }
1002 return false;
1003 }
1004 Set<String> finalApproverNodeNames = new HashSet<String>();
1005 for (RouteNode node : finalApproverNodes) {
1006 finalApproverNodeNames.add(node.getRouteNodeName());
1007 }
1008
1009 int approveRequest = 0;
1010 for (ActionRequestValue request : requests) {
1011 RouteNodeInstance nodeInstance = request.getNodeInstance();
1012 if (nodeInstance == null) {
1013 if ( LOG.isDebugEnabled() ) {
1014 LOG.debug("Found an action request on the document with a null node instance, indicating EXCEPTION routing.");
1015 }
1016 return false;
1017 }
1018 if (finalApproverNodeNames.contains(nodeInstance.getRouteNode().getRouteNodeName())) {
1019 if (request.isApproveOrCompleteRequest()) {
1020 approveRequest++;
1021 if ( LOG.isDebugEnabled() ) {
1022 LOG.debug("Found request is approver " + request.getActionRequestId());
1023 }
1024 if (! request.isRecipientRoutedRequest(principalId)) {
1025 if ( LOG.isDebugEnabled() ) {
1026 LOG.debug("Action Request not for user " + principalId);
1027 }
1028 return false;
1029 }
1030 }
1031 }
1032 }
1033
1034 if (approveRequest == 0) {
1035 return false;
1036 }
1037 if ( LOG.isDebugEnabled() ) {
1038 LOG.debug("Principal "+principalId+" is final approver for document " + documentId);
1039 }
1040 return true;
1041 }
1042
1043 @Override
1044 public boolean routeNodeHasApproverActionRequest(String documentTypeName, String docContent, String nodeName) {
1045 incomingParamCheck(documentTypeName, "documentTypeName");
1046 incomingParamCheck(docContent, "docContent");
1047 incomingParamCheck(nodeName, "nodeName");
1048 if ( LOG.isDebugEnabled() ) {
1049 LOG.debug("Evaluating routeNodeHasApproverActionRequest [docTypeName=" + documentTypeName + ", nodeName=" + nodeName + "]");
1050 }
1051 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName);
1052 RouteNode routeNode = KEWServiceLocator.getRouteNodeService().findRouteNodeByName(documentType.getDocumentTypeId(), nodeName);
1053 return routeNodeHasApproverActionRequest(documentType, docContent, routeNode, new Integer(KewApiConstants.INVALID_ROUTE_LEVEL));
1054 }
1055
1056 /**
1057 * Really this method needs to be implemented using the executeSimulation functionality (the SimulationEngine).
1058 * This would get rid of the needs for us to call to FlexRM directly.
1059 */
1060 private boolean routeNodeHasApproverActionRequest(DocumentType documentType, String docContent, RouteNode node, Integer routeLevel) {
1061 incomingParamCheck(documentType, "documentType");
1062 incomingParamCheck(docContent, "docContent");
1063 incomingParamCheck(node, "node");
1064 incomingParamCheck(routeLevel, "routeLevel");
1065
1066 /* DocumentRouteHeaderValue routeHeader = new DocumentRouteHeaderValue();
1067 routeHeader.setDocumentId("");
1068 routeHeader.setDocumentTypeId(documentType.getDocumentTypeId());
1069 routeHeader.setDocRouteLevel(routeLevel);
1070 routeHeader.setDocVersion(new Integer(KewApiConstants.DocumentContentVersions.CURRENT));*/
1071
1072 //TODO THIS NEEDS TESTING!!!!! IT WAS A GUESS ON HOW THIS WORKS
1073 RoutingReportCriteria.Builder builder = RoutingReportCriteria.Builder.createByDocumentTypeName(documentType.getName());
1074 builder.setTargetNodeName(node.getName());
1075 builder.setXmlContent(docContent);
1076 DocumentDetail docDetail = executeSimulation(builder.build());
1077 if (docDetail != null) {
1078 for (ActionRequest actionRequest : docDetail.getActionRequests()) {
1079 if (actionRequest.isApprovalRequest()) {
1080 return true;
1081 }
1082 }
1083 }
1084 /*if (node.getRuleTemplate() != null && node.isFlexRM()) {
1085 String ruleTemplateName = node.getRuleTemplate().getName();
1086 builder.setXmlContent(docContent);
1087 routeHeader.setDocRouteStatus(KewApiConstants.ROUTE_HEADER_INITIATED_CD);
1088 FlexRM flexRM = new FlexRM();
1089 RouteContext context = RouteContext.getCurrentRouteContext();
1090 context.setDocument(routeHeader);
1091 try {
1092 List actionRequests = flexRM.getActionRequests(routeHeader, node, null, ruleTemplateName);
1093 for (Iterator iter = actionRequests.iterator(); iter.hasNext();) {
1094 ActionRequestValue actionRequest = (ActionRequestValue) iter.next();
1095 if (actionRequest.isApproveOrCompleteRequest()) {
1096 return true;
1097 }
1098 }
1099 } finally {
1100 RouteContext.clearCurrentRouteContext();
1101 }
1102 }*/
1103 return false;
1104 }
1105
1106 @Override
1107 public boolean isLastApproverAtNode(String documentId, String principalId, String nodeName) {
1108 incomingParamCheck(documentId, "documentId");
1109 incomingParamCheck(principalId, "principalId");
1110 incomingParamCheck(nodeName, "nodeName");
1111 if ( LOG.isDebugEnabled() ) {
1112 LOG.debug("Evaluating isLastApproverAtNode [docId=" + documentId + ", principalId=" + principalId + ", nodeName=" + nodeName + "]");
1113 }
1114 loadDocument(documentId);
1115 // If this app constant is set to true, then we will attempt to simulate activation of non-active requests before
1116 // attempting to deactivate them, this is in order to address the force action issue reported by EPIC in issue
1117 // http://fms.dfa.cornell.edu:8080/browse/KULWF-366
1118 Boolean activateFirst = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean(
1119 KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.FEATURE_DETAIL_TYPE, KewApiConstants.IS_LAST_APPROVER_ACTIVATE_FIRST_IND);
1120 if (activateFirst == null) {
1121 activateFirst = Boolean.FALSE;
1122 }
1123
1124 List<ActionRequestValue> requests = KEWServiceLocator.getActionRequestService().findPendingByDocRequestCdNodeName(documentId, KewApiConstants.ACTION_REQUEST_APPROVE_REQ, nodeName);
1125 if (requests == null || requests.isEmpty()) {
1126 return false;
1127 }
1128
1129 // Deep-copy the action requests for the simulation.
1130 List<ActionRequestValue> copiedRequests = new ArrayList<ActionRequestValue>();
1131 for (ActionRequestValue request : requests) {
1132 ActionRequestValue actionRequest = (ActionRequestValue) ObjectUtils.deepCopy(
1133 (ActionRequestValue) request);
1134 // Deep-copy the action items as well, since they are indirectly retrieved from the action request via service calls.
1135 for (ActionItem actionItem : actionRequest.getActionItems()) {
1136 actionRequest.getSimulatedActionItems().add((ActionItem) ObjectUtils.deepCopy(actionItem));
1137 }
1138 copiedRequests.add(actionRequest);
1139 }
1140
1141 ActivationContext activationContext = new ActivationContext(ActivationContext.CONTEXT_IS_SIMULATION);
1142 for (ActionRequestValue request : copiedRequests) {
1143 if (activateFirst.booleanValue() && !request.isActive()) {
1144 KEWServiceLocator.getActionRequestService().activateRequest(request, activationContext);
1145 }
1146 if (request.isUserRequest() && request.getPrincipalId().equals(principalId)) {
1147 KEWServiceLocator.getActionRequestService().deactivateRequest(null, request, activationContext);
1148 } else if (request.isGroupRequest() && KimApiServiceLocator.getGroupService().isMemberOfGroup(principalId, request.getGroup().getId())) {
1149 KEWServiceLocator.getActionRequestService().deactivateRequest(null, request, activationContext);
1150 }
1151 }
1152 boolean allDeactivated = true;
1153 for (ActionRequestValue actionRequest: copiedRequests) {
1154 allDeactivated = allDeactivated && actionRequest.isDeactivated();
1155 }
1156 return allDeactivated;
1157 }
1158
1159 @Override
1160 public boolean isUserInRouteLog(String documentId, String principalId, boolean lookFuture) {
1161 incomingParamCheck(documentId, "documentId");
1162 incomingParamCheck(principalId, "principalId");
1163 return isUserInRouteLogWithOptionalFlattening(documentId, principalId, lookFuture, false);
1164 }
1165
1166 @Override
1167 public boolean isUserInRouteLogWithOptionalFlattening(String documentId, String principalId, boolean lookFuture, boolean flattenNodes) {
1168 incomingParamCheck(documentId, "documentId");
1169 incomingParamCheck(principalId, "principalId");
1170 boolean authorized = false;
1171 if ( LOG.isDebugEnabled() ) {
1172 LOG.debug("Evaluating isUserInRouteLog [docId=" + documentId + ", principalId=" + principalId + ", lookFuture=" + lookFuture + "]");
1173 }
1174 DocumentRouteHeaderValue routeHeader = loadDocument(documentId);
1175 if (routeHeader == null) {
1176 throw new IllegalArgumentException("Document for documentId: " + documentId + " does not exist");
1177 }
1178 Principal principal = KEWServiceLocator.getIdentityHelperService().getPrincipal(principalId);
1179 if (principal == null) {
1180 throw new IllegalArgumentException("Principal for principalId: " + principalId + " does not exist");
1181 }
1182 List<ActionTakenValue> actionsTaken = KEWServiceLocator.getActionTakenService().findByDocumentIdWorkflowId(documentId, principal.getPrincipalId());
1183
1184 if(routeHeader.getInitiatorWorkflowId().equals(principal.getPrincipalId())){
1185 return true;
1186 }
1187
1188 if (!actionsTaken.isEmpty()) {
1189 LOG.debug("found action taken by user");
1190 authorized = true;
1191 }
1192
1193 List<ActionRequestValue> actionRequests = KEWServiceLocator.getActionRequestService().findAllActionRequestsByDocumentId(documentId);
1194 if (actionRequestListHasPrincipal(principal, actionRequests)) {
1195 authorized = true;
1196 }
1197
1198 if (!lookFuture || authorized) {
1199 return authorized;
1200 }
1201
1202
1203 SimulationWorkflowEngine simulationEngine = KEWServiceLocator.getSimulationEngine();
1204 SimulationCriteria criteria = SimulationCriteria.createSimulationCritUsingDocumentId(documentId);
1205 criteria.setDestinationNodeName(null); // process entire document to conclusion
1206 criteria.getDestinationRecipients().add(new KimPrincipalRecipient(principal));
1207 criteria.setFlattenNodes(flattenNodes);
1208
1209 try {
1210 SimulationResults results = simulationEngine.runSimulation(criteria);
1211 if (actionRequestListHasPrincipal(principal, results.getSimulatedActionRequests())) {
1212 authorized = true;
1213 }
1214 } catch (Exception e) {
1215 throw new RiceRuntimeException(e);
1216 }
1217
1218 return authorized;
1219 }
1220
1221 private boolean actionRequestListHasPrincipal(Principal principal, List<ActionRequestValue> actionRequests) {
1222 for (ActionRequestValue actionRequest : actionRequests) {
1223 if (actionRequest.isRecipientRoutedRequest(new KimPrincipalRecipient(principal))) {
1224 return true;
1225 }
1226 }
1227 return false;
1228 }
1229
1230 public List<String> getPrincipalIdsInRouteLog(String documentId, boolean lookFuture) {
1231 if (StringUtils.isEmpty(documentId)) {
1232 throw new IllegalArgumentException("documentId passed in is null or blank");
1233 }
1234 Set<String> principalIds = new HashSet<String>();
1235 try {
1236 if ( LOG.isDebugEnabled() ) {
1237 LOG.debug("Evaluating isUserInRouteLog [docId=" + documentId + ", lookFuture=" + lookFuture + "]");
1238 }
1239 DocumentRouteHeaderValue routeHeader = loadDocument(documentId);
1240 List<ActionTakenValue> actionsTakens =
1241 (List<ActionTakenValue>)KEWServiceLocator.getActionTakenService().findByDocumentId(documentId);
1242 //TODO: confirm that the initiator is not already there in the actionstaken
1243 principalIds.add(routeHeader.getInitiatorWorkflowId());
1244 for(ActionTakenValue actionTaken: actionsTakens){
1245 principalIds.add(actionTaken.getPrincipalId());
1246 }
1247 List<ActionRequestValue> actionRequests =
1248 KEWServiceLocator.getActionRequestService().findAllActionRequestsByDocumentId(documentId);
1249 for(ActionRequestValue actionRequest: actionRequests){
1250 principalIds.addAll(getPrincipalIdsForActionRequest(actionRequest));
1251 }
1252 if (!lookFuture) {
1253 return new ArrayList<String>(principalIds);
1254 }
1255 SimulationWorkflowEngine simulationEngine = KEWServiceLocator.getSimulationEngine();
1256 SimulationCriteria criteria = SimulationCriteria.createSimulationCritUsingDocumentId(documentId);
1257 criteria.setDestinationNodeName(null); // process entire document to conclusion
1258 SimulationResults results = simulationEngine.runSimulation(criteria);
1259 actionRequests = (List<ActionRequestValue>)results.getSimulatedActionRequests();
1260 for(ActionRequestValue actionRequest: actionRequests){
1261 principalIds.addAll(getPrincipalIdsForActionRequest(actionRequest));
1262 }
1263 } catch (Exception ex) {
1264 LOG.warn("Problems getting principalIds in Route Log for documentId: "+documentId+". Exception:"+ex.getMessage(),ex);
1265 }
1266 return new ArrayList<String>(principalIds);
1267 }
1268
1269 private DocumentRouteHeaderValue loadDocument(String documentId) {
1270 return KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId);
1271 }
1272
1273 /**
1274 * This method gets all of the principalIds for the given ActionRequestValue. It drills down into
1275 * groups if need be.
1276 *
1277 * @param actionRequest
1278 */
1279 private List<String> getPrincipalIdsForActionRequest(ActionRequestValue actionRequest) {
1280 List<String> results = Collections.emptyList();
1281 if (actionRequest.getPrincipalId() != null) {
1282 results = Collections.singletonList(actionRequest.getPrincipalId());
1283 } else if (actionRequest.getGroupId() != null) {
1284 List<String> principalIdsForGroup =
1285 KimApiServiceLocator.getGroupService().getMemberPrincipalIds(actionRequest.getGroupId());
1286 if (principalIdsForGroup != null) {
1287 results = principalIdsForGroup;
1288 }
1289 }
1290 return results;
1291 }
1292
1293 private void incomingParamCheck(Object object, String name) {
1294 if (object == null) {
1295 throw new RiceIllegalArgumentException(name + " was null");
1296 } else if (object instanceof String
1297 && StringUtils.isBlank((String) object)) {
1298 throw new RiceIllegalArgumentException(name + " was blank");
1299 }
1300 }
1301
1302 public void setDocumentTypeService(DocumentTypeService documentTypeService) {
1303 this.documentTypeService = documentTypeService;
1304 }
1305
1306 /**
1307 * TODO - this code is temporary until we get rid of all the crazy throwing of
1308 * "WorkflowException"
1309 */
1310 private void translateException(WorkflowException e) {
1311 if (e instanceof org.kuali.rice.kew.api.exception.InvalidActionTakenException) {
1312 throw new InvalidActionTakenException(e.getMessage(), e);
1313 }
1314 throw new WorkflowRuntimeException(e.getMessage(), e);
1315 }
1316
1317 protected DocumentActionResult executeActionInternal(DocumentActionParameters parameters,
1318 DocumentActionCallback callback) {
1319 if (parameters == null) {
1320 throw new RiceIllegalArgumentException("Document action parameters was null.");
1321 }
1322 if (LOG.isDebugEnabled()) {
1323 LOG.debug(callback.getLogMessage(parameters.getDocumentId(), parameters.getPrincipalId(),
1324 parameters.getAnnotation()));
1325 }
1326 DocumentRouteHeaderValue documentBo = init(parameters);
1327 try {
1328 documentBo = callback.doInDocumentBo(documentBo, parameters.getPrincipalId(), parameters.getAnnotation());
1329 } catch (WorkflowException e) {
1330 // TODO fix this up once the checked exception goes away
1331 translateException(e);
1332 }
1333 return constructDocumentActionResult(documentBo, parameters.getPrincipalId());
1334 }
1335
1336 protected static interface DocumentActionCallback {
1337
1338 DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId,
1339 String annotation) throws WorkflowException;
1340
1341 String getLogMessage(String documentId, String principalId, String annotation);
1342
1343 }
1344
1345 protected static abstract class StandardDocumentActionCallback implements DocumentActionCallback {
1346
1347 public final String getLogMessage(String documentId, String principalId, String annotation) {
1348 return getActionName() + " [principalId=" + principalId + ", documentId=" + documentId + ", annotation="
1349 + annotation + "]";
1350 }
1351
1352 protected abstract String getActionName();
1353
1354 }
1355
1356 }