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