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