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.RemotableAttributeErrorContract; 025import org.kuali.rice.core.api.uif.RemotableAttributeError; 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.KewApiServiceLocator; 033import org.kuali.rice.kew.api.WorkflowRuntimeException; 034import org.kuali.rice.kew.api.action.ActionRequest; 035import org.kuali.rice.kew.api.action.ActionRequestType; 036import org.kuali.rice.kew.api.action.ActionType; 037import org.kuali.rice.kew.api.action.AdHocRevoke; 038import org.kuali.rice.kew.api.action.AdHocToGroup; 039import org.kuali.rice.kew.api.action.AdHocToGroup_v2_1_2; 040import org.kuali.rice.kew.api.action.AdHocToPrincipal; 041import org.kuali.rice.kew.api.action.AdHocToPrincipal_v2_1_2; 042import org.kuali.rice.kew.api.action.DocumentActionParameters; 043import org.kuali.rice.kew.api.action.DocumentActionResult; 044import org.kuali.rice.kew.api.action.InvalidActionTakenException; 045import org.kuali.rice.kew.api.action.MovePoint; 046import org.kuali.rice.kew.api.action.RequestedActions; 047import org.kuali.rice.kew.api.action.ReturnPoint; 048import org.kuali.rice.kew.api.action.RoutingReportCriteria; 049import org.kuali.rice.kew.api.action.ValidActions; 050import org.kuali.rice.kew.api.action.WorkflowDocumentActionsService; 051import org.kuali.rice.kew.api.doctype.DocumentTypeService; 052import org.kuali.rice.kew.api.doctype.IllegalDocumentTypeException; 053import org.kuali.rice.kew.api.document.Document; 054import org.kuali.rice.kew.api.document.DocumentContentUpdate; 055import org.kuali.rice.kew.api.document.DocumentDetail; 056import org.kuali.rice.kew.api.document.DocumentUpdate; 057import org.kuali.rice.kew.api.document.PropertyDefinition; 058import org.kuali.rice.kew.api.document.attribute.WorkflowAttributeDefinition; 059import org.kuali.rice.kew.api.exception.WorkflowException; 060import org.kuali.rice.kew.definition.AttributeDefinition; 061import org.kuali.rice.kew.doctype.bo.DocumentType; 062import org.kuali.rice.kew.dto.DTOConverter; 063import org.kuali.rice.kew.engine.ActivationContext; 064import org.kuali.rice.kew.engine.node.RouteNode; 065import org.kuali.rice.kew.engine.node.RouteNodeInstance; 066import org.kuali.rice.kew.engine.simulation.SimulationCriteria; 067import org.kuali.rice.kew.engine.simulation.SimulationResults; 068import org.kuali.rice.kew.engine.simulation.SimulationWorkflowEngine; 069import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 070import org.kuali.rice.kew.rule.WorkflowRuleAttribute; 071import org.kuali.rice.kew.rule.WorkflowAttributeXmlValidator; 072import org.kuali.rice.kew.rule.bo.RuleAttribute; 073import org.kuali.rice.kew.rule.xmlrouting.GenericXMLRuleAttribute; 074import org.kuali.rice.kew.service.KEWServiceLocator; 075import org.kuali.rice.kew.api.KewApiConstants; 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 // TODO ewestfal - THIS METHOD NEEDS JAVADOCS 766 throw new UnsupportedOperationException("implement me!!!"); 767 } 768 769 @Override 770 public DocumentActionResult superUserBlanketApprove(DocumentActionParameters parameters, 771 final boolean executePostProcessor) { 772 incomingParamCheck(parameters, "parameters"); 773 return executeActionInternal(parameters, 774 new DocumentActionCallback() { 775 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, 776 String principalId, String annotation) throws WorkflowException { 777 return KEWServiceLocator.getWorkflowDocumentService().superUserApprove(principalId, documentBo, 778 annotation, executePostProcessor); 779 } 780 781 public String getLogMessage(String documentId, String principalId, String annotation) { 782 return "SU Blanket Approve [principalId=" + principalId + ", documentId=" + documentId 783 + ", annotation=" + annotation + "]"; 784 } 785 }); 786 } 787 788 @Override 789 public DocumentActionResult superUserNodeApprove(DocumentActionParameters parameters, 790 final boolean executePostProcessor, final String nodeName) { 791 incomingParamCheck(parameters, "parameters"); 792 incomingParamCheck(nodeName, "nodeName"); 793 return executeActionInternal(parameters, 794 new DocumentActionCallback() { 795 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, 796 String principalId, String annotation) throws WorkflowException { 797 return KEWServiceLocator.getWorkflowDocumentService().superUserNodeApproveAction(principalId, 798 documentBo, nodeName, annotation, executePostProcessor); 799 } 800 801 public String getLogMessage(String documentId, String principalId, String annotation) { 802 return "SU Node Approve Action [principalId=" + principalId + ", documentId=" + documentId 803 + ", nodeName=" + nodeName + ", annotation=" + annotation + "]"; 804 } 805 }); 806 807 } 808 809 @Override 810 public DocumentActionResult superUserTakeRequestedAction(DocumentActionParameters parameters, 811 final boolean executePostProcessor, final String actionRequestId) { 812 incomingParamCheck(parameters, "parameters"); 813 incomingParamCheck(actionRequestId, "actionRequestId"); 814 return executeActionInternal(parameters, 815 new DocumentActionCallback() { 816 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, 817 String principalId, String annotation) throws WorkflowException { 818 return KEWServiceLocator.getWorkflowDocumentService().superUserActionRequestApproveAction( 819 principalId, documentBo, actionRequestId, annotation, 820 executePostProcessor); 821 } 822 823 public String getLogMessage(String documentId, String principalId, String annotation) { 824 return "SU Take Requested Action [principalId=" + principalId + ", docume tId=" + documentId 825 + ", actionRequestId=" + actionRequestId + ", annotation=" + annotation + "]"; 826 } 827 }); 828 } 829 830 @Override 831 public DocumentActionResult superUserDisapprove(DocumentActionParameters parameters, 832 final boolean executePostProcessor) { 833 incomingParamCheck(parameters, "parameters"); 834 return executeActionInternal(parameters, 835 new DocumentActionCallback() { 836 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, 837 String principalId, String annotation) throws WorkflowException { 838 return KEWServiceLocator.getWorkflowDocumentService().superUserDisapproveAction(principalId, 839 documentBo, annotation, executePostProcessor); 840 } 841 842 public String getLogMessage(String documentId, String principalId, String annotation) { 843 return "SU Disapprove [principalId=" + principalId + ", documentId=" + documentId 844 + ", annotation=" + annotation + "]"; 845 } 846 }); 847 } 848 849 @Override 850 public DocumentActionResult superUserCancel(DocumentActionParameters parameters, final boolean executePostProcessor) { 851 incomingParamCheck(parameters, "parameters"); 852 return executeActionInternal(parameters, 853 new DocumentActionCallback() { 854 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, 855 String principalId, String annotation) throws WorkflowException { 856 return KEWServiceLocator.getWorkflowDocumentService().superUserCancelAction(principalId, 857 documentBo, annotation, executePostProcessor); 858 } 859 860 public String getLogMessage(String documentId, String principalId, String annotation) { 861 return "SU Cancel [principalId=" + principalId + ", documentId=" + documentId + ", annotation=" 862 + annotation + "]"; 863 } 864 }); 865 } 866 867 @Override 868 public DocumentActionResult superUserReturnToPreviousNode(DocumentActionParameters parameters, 869 final boolean executePostProcessor, final ReturnPoint returnPoint) { 870 incomingParamCheck(parameters, "parameters"); 871 incomingParamCheck(returnPoint, "returnPoint"); 872 return executeActionInternal(parameters, 873 new DocumentActionCallback() { 874 public DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, 875 String principalId, String annotation) throws WorkflowException { 876 return KEWServiceLocator.getWorkflowDocumentService().superUserReturnDocumentToPreviousNode( 877 principalId, documentBo, returnPoint.getNodeName(), annotation, executePostProcessor); 878 } 879 880 public String getLogMessage(String documentId, String principalId, String annotation) { 881 return "SU Return to Previous Node [principalId=" + principalId + ", documentId=" + documentId 882 + ", annotation=" + annotation + ", returnPoint=" + returnPoint + "]"; 883 } 884 }); 885 886 } 887 888 @Override 889 public DocumentActionResult placeInExceptionRouting(DocumentActionParameters parameters) { 890 incomingParamCheck(parameters, "parameters"); 891 return executeActionInternal(parameters, PLACE_IN_EXCEPTION_CALLBACK); 892 } 893 894 @Override 895 public boolean documentWillHaveAtLeastOneActionRequest(RoutingReportCriteria reportCriteria, List<String> actionRequestedCodes, boolean ignoreCurrentActionRequests) { 896 incomingParamCheck(reportCriteria, "reportCriteria"); 897 incomingParamCheck(actionRequestedCodes, "actionRequestedCodes"); 898 try { 899 SimulationWorkflowEngine simulationEngine = KEWServiceLocator.getSimulationEngine(); 900 SimulationCriteria criteria = SimulationCriteria.from(reportCriteria); 901 // set activate requests to true by default so force action works correctly 902 criteria.setActivateRequests(Boolean.TRUE); 903 SimulationResults results = simulationEngine.runSimulation(criteria); 904 List<ActionRequestValue> actionRequestsToProcess = results.getSimulatedActionRequests(); 905 if (!ignoreCurrentActionRequests) { 906 actionRequestsToProcess.addAll(results.getDocument().getActionRequests()); 907 } 908 for (ActionRequestValue actionRequest : actionRequestsToProcess) { 909 if (actionRequest.isDone()) { 910 // an action taken has eliminated this request from being active 911 continue; 912 } 913 // if no action request codes are passed in.... assume any request found is 914 if (CollectionUtils.isEmpty(actionRequestedCodes) ) { 915 // we found an action request 916 return true; 917 } 918 // check the action requested codes passed in 919 for (String requestedActionRequestCode : actionRequestedCodes) { 920 if (requestedActionRequestCode.equals(actionRequest.getActionRequested())) { 921 boolean satisfiesDestinationUserCriteria = (criteria.getDestinationRecipients().isEmpty()) || (isRecipientRoutedRequest(actionRequest,criteria.getDestinationRecipients())); 922 if (satisfiesDestinationUserCriteria) { 923 if (StringUtils.isBlank(criteria.getDestinationNodeName())) { 924 return true; 925 } else if (StringUtils.equals(criteria.getDestinationNodeName(),actionRequest.getNodeInstance().getName())) { 926 return true; 927 } 928 } 929 } 930 } 931 } 932 return false; 933 } catch (Exception ex) { 934 String error = "Problems evaluating documentWillHaveAtLeastOneActionRequest: " + ex.getMessage(); 935 LOG.error(error,ex); 936 if (ex instanceof RuntimeException) { 937 throw (RuntimeException)ex; 938 } 939 throw new RuntimeException(error, ex); 940 } 941 } 942 943 private boolean isRecipientRoutedRequest(ActionRequestValue actionRequest, List<Recipient> recipients) throws WorkflowException { 944 for (Recipient recipient : recipients) { 945 if (actionRequest.isRecipientRoutedRequest(recipient)) { 946 return true; 947 } 948 } 949 return false; 950 } 951 952 @Override 953 public void reResolveRoleByDocTypeName(String documentTypeName, String roleName, String qualifiedRoleNameLabel) { 954 incomingParamCheck(documentTypeName, "documentTypeName"); 955 incomingParamCheck(roleName, "roleName"); 956 incomingParamCheck(qualifiedRoleNameLabel, "qualifiedRoleNameLabel"); 957 if ( LOG.isDebugEnabled() ) { 958 LOG.debug("Re-resolving Role [docTypeName=" + documentTypeName + ", roleName=" + roleName + ", qualifiedRoleNameLabel=" + qualifiedRoleNameLabel + "]"); 959 } 960 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName); 961 if (org.apache.commons.lang.StringUtils.isEmpty(qualifiedRoleNameLabel)) { 962 KEWServiceLocator.getRoleService().reResolveRole(documentType, roleName); 963 } else { 964 KEWServiceLocator.getRoleService().reResolveQualifiedRole(documentType, roleName, qualifiedRoleNameLabel); 965 } 966 } 967 968 public void reResolveRoleByDocumentId(String documentId, String roleName, String qualifiedRoleNameLabel) { 969 incomingParamCheck(documentId, "documentId"); 970 incomingParamCheck(roleName, "roleName"); 971 incomingParamCheck(qualifiedRoleNameLabel, "qualifiedRoleNameLabel"); 972 if ( LOG.isDebugEnabled() ) { 973 LOG.debug("Re-resolving Role [documentId=" + documentId + ", roleName=" + roleName + ", qualifiedRoleNameLabel=" + qualifiedRoleNameLabel + "]"); 974 } 975 DocumentRouteHeaderValue routeHeader = loadDocument(documentId); 976 if (org.apache.commons.lang.StringUtils.isEmpty(qualifiedRoleNameLabel)) { 977 KEWServiceLocator.getRoleService().reResolveRole(routeHeader, roleName); 978 } else { 979 KEWServiceLocator.getRoleService().reResolveQualifiedRole(routeHeader, roleName, qualifiedRoleNameLabel); 980 } 981 } 982 983 @Override 984 public List<RemotableAttributeError> validateWorkflowAttributeDefinition( 985 WorkflowAttributeDefinition definition) { 986 if (definition == null) { 987 throw new RiceIllegalArgumentException("definition was null"); 988 } 989 if ( LOG.isDebugEnabled() ) { 990 LOG.debug("Validating WorkflowAttributeDefinition [attributeName="+definition.getAttributeName()+"]"); 991 } 992 AttributeDefinition attributeDefinition = DTOConverter.convertWorkflowAttributeDefinition(definition); 993 WorkflowRuleAttribute attribute = null; 994 if (attributeDefinition != null) { 995 attribute = (WorkflowRuleAttribute) GlobalResourceLoader.getObject(attributeDefinition.getObjectDefinition()); 996 } 997 if (attribute instanceof GenericXMLRuleAttribute) { 998 Map<String, String> attributePropMap = new HashMap<String, String>(); 999 GenericXMLRuleAttribute xmlAttribute = (GenericXMLRuleAttribute)attribute; 1000 xmlAttribute.setExtensionDefinition(attributeDefinition.getExtensionDefinition()); 1001 for (PropertyDefinition propertyDefinition : definition.getPropertyDefinitions()) { 1002 attributePropMap.put(propertyDefinition.getName(), propertyDefinition.getValue()); 1003 } 1004 xmlAttribute.setParamMap(attributePropMap); 1005 } 1006 List<RemotableAttributeError> errors = new ArrayList<RemotableAttributeError>(); 1007 //validate inputs from client application if the attribute is capable 1008 if (attribute instanceof WorkflowAttributeXmlValidator) { 1009 List<? extends RemotableAttributeErrorContract> validationErrors = ((WorkflowAttributeXmlValidator)attribute).validateClientRoutingData(); 1010 if (validationErrors != null) { 1011 for (RemotableAttributeErrorContract validationError : validationErrors) { 1012 errors.add(RemotableAttributeError.Builder.create(validationError).build()); 1013 } 1014 } 1015 } 1016 return errors; 1017 } 1018 1019 @Override 1020 public boolean isFinalApprover(String documentId, String principalId) { 1021 incomingParamCheck(documentId, "documentId"); 1022 incomingParamCheck(principalId, "principalId"); 1023 if ( LOG.isDebugEnabled() ) { 1024 LOG.debug("Evaluating isFinalApprover [docId=" + documentId + ", principalId=" + principalId + "]"); 1025 } 1026 DocumentRouteHeaderValue routeHeader = loadDocument(documentId); 1027 List<ActionRequestValue> requests = KEWServiceLocator.getActionRequestService().findPendingByDoc(documentId); 1028 List<RouteNode> finalApproverNodes = KEWServiceLocator.getRouteNodeService().findFinalApprovalRouteNodes(routeHeader.getDocumentType().getDocumentTypeId()); 1029 if (finalApproverNodes.isEmpty()) { 1030 if ( LOG.isDebugEnabled() ) { 1031 LOG.debug("Could not locate final approval nodes for document " + documentId); 1032 } 1033 return false; 1034 } 1035 Set<String> finalApproverNodeNames = new HashSet<String>(); 1036 for (RouteNode node : finalApproverNodes) { 1037 finalApproverNodeNames.add(node.getRouteNodeName()); 1038 } 1039 1040 int approveRequest = 0; 1041 for (ActionRequestValue request : requests) { 1042 RouteNodeInstance nodeInstance = request.getNodeInstance(); 1043 if (nodeInstance == null) { 1044 if ( LOG.isDebugEnabled() ) { 1045 LOG.debug("Found an action request on the document with a null node instance, indicating EXCEPTION routing."); 1046 } 1047 return false; 1048 } 1049 if (finalApproverNodeNames.contains(nodeInstance.getRouteNode().getRouteNodeName())) { 1050 if (request.isApproveOrCompleteRequest()) { 1051 approveRequest++; 1052 if ( LOG.isDebugEnabled() ) { 1053 LOG.debug("Found request is approver " + request.getActionRequestId()); 1054 } 1055 if (! request.isRecipientRoutedRequest(principalId)) { 1056 if ( LOG.isDebugEnabled() ) { 1057 LOG.debug("Action Request not for user " + principalId); 1058 } 1059 return false; 1060 } 1061 } 1062 } 1063 } 1064 1065 if (approveRequest == 0) { 1066 return false; 1067 } 1068 if ( LOG.isDebugEnabled() ) { 1069 LOG.debug("Principal "+principalId+" is final approver for document " + documentId); 1070 } 1071 return true; 1072 } 1073 1074 @Override 1075 public boolean routeNodeHasApproverActionRequest(String documentTypeName, String docContent, String nodeName) { 1076 incomingParamCheck(documentTypeName, "documentTypeName"); 1077 incomingParamCheck(docContent, "docContent"); 1078 incomingParamCheck(nodeName, "nodeName"); 1079 if ( LOG.isDebugEnabled() ) { 1080 LOG.debug("Evaluating routeNodeHasApproverActionRequest [docTypeName=" + documentTypeName + ", nodeName=" + nodeName + "]"); 1081 } 1082 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName); 1083 RouteNode routeNode = KEWServiceLocator.getRouteNodeService().findRouteNodeByName(documentType.getDocumentTypeId(), nodeName); 1084 return routeNodeHasApproverActionRequest(documentType, docContent, routeNode, new Integer(KewApiConstants.INVALID_ROUTE_LEVEL)); 1085 } 1086 1087 /** 1088 * Really this method needs to be implemented using the executeSimulation functionality (the SimulationEngine). 1089 * This would get rid of the needs for us to call to FlexRM directly. 1090 */ 1091 private boolean routeNodeHasApproverActionRequest(DocumentType documentType, String docContent, RouteNode node, Integer routeLevel) { 1092 incomingParamCheck(documentType, "documentType"); 1093 incomingParamCheck(docContent, "docContent"); 1094 incomingParamCheck(node, "node"); 1095 incomingParamCheck(routeLevel, "routeLevel"); 1096 1097/* DocumentRouteHeaderValue routeHeader = new DocumentRouteHeaderValue(); 1098 routeHeader.setDocumentId(""); 1099 routeHeader.setDocumentTypeId(documentType.getDocumentTypeId()); 1100 routeHeader.setDocRouteLevel(routeLevel); 1101 routeHeader.setDocVersion(new Integer(KewApiConstants.DocumentContentVersions.CURRENT));*/ 1102 1103 RoutingReportCriteria.Builder builder = RoutingReportCriteria.Builder.createByDocumentTypeName(documentType.getName()); 1104 builder.setNodeNames(Collections.singletonList(node.getName())); 1105 builder.setXmlContent(docContent); 1106 DocumentDetail docDetail = executeSimulation(builder.build()); 1107 if (docDetail != null) { 1108 for (ActionRequest actionRequest : docDetail.getActionRequests()) { 1109 if (actionRequest.isApprovalRequest()) { 1110 return true; 1111 } 1112 } 1113 } 1114 /*if (node.getRuleTemplate() != null && node.isFlexRM()) { 1115 String ruleTemplateName = node.getRuleTemplate().getName(); 1116 builder.setXmlContent(docContent); 1117 routeHeader.setDocRouteStatus(KewApiConstants.ROUTE_HEADER_INITIATED_CD); 1118 FlexRM flexRM = new FlexRM(); 1119 RouteContext context = RouteContext.getCurrentRouteContext(); 1120 context.setDocument(routeHeader); 1121 try { 1122 List actionRequests = flexRM.getActionRequests(routeHeader, node, null, ruleTemplateName); 1123 for (Iterator iter = actionRequests.iterator(); iter.hasNext();) { 1124 ActionRequestValue actionRequest = (ActionRequestValue) iter.next(); 1125 if (actionRequest.isApproveOrCompleteRequest()) { 1126 return true; 1127 } 1128 } 1129 } finally { 1130 RouteContext.clearCurrentRouteContext(); 1131 } 1132 }*/ 1133 return false; 1134 } 1135 1136 @Override 1137 public boolean isLastApproverAtNode(String documentId, String principalId, String nodeName) { 1138 incomingParamCheck(documentId, "documentId"); 1139 incomingParamCheck(principalId, "principalId"); 1140 incomingParamCheck(nodeName, "nodeName"); 1141 if ( LOG.isDebugEnabled() ) { 1142 LOG.debug("Evaluating isLastApproverAtNode [docId=" + documentId + ", principalId=" + principalId + ", nodeName=" + nodeName + "]"); 1143 } 1144 loadDocument(documentId); 1145 // If this app constant is set to true, then we will attempt to simulate activation of non-active requests before 1146 // attempting to deactivate them, this is in order to address the force action issue reported by EPIC in issue 1147 // http://fms.dfa.cornell.edu:8080/browse/KULWF-366 1148 Boolean activateFirst = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsBoolean( 1149 KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.FEATURE_DETAIL_TYPE, KewApiConstants.IS_LAST_APPROVER_ACTIVATE_FIRST_IND); 1150 if (activateFirst == null) { 1151 activateFirst = Boolean.FALSE; 1152 } 1153 1154 List<ActionRequestValue> requests = KEWServiceLocator.getActionRequestService().findPendingByDocRequestCdNodeName(documentId, KewApiConstants.ACTION_REQUEST_APPROVE_REQ, nodeName); 1155 if (requests == null || requests.isEmpty()) { 1156 return false; 1157 } 1158 1159 // Deep-copy the action requests for the simulation. 1160 List<ActionRequestValue> copiedRequests = new ArrayList<ActionRequestValue>(); 1161 for (ActionRequestValue request : requests) { 1162 ActionRequestValue actionRequest = (ActionRequestValue) ObjectUtils.deepCopy( 1163 (ActionRequestValue) request); 1164 // Deep-copy the action items as well, since they are indirectly retrieved from the action request via service calls. 1165 for (ActionItem actionItem : actionRequest.getActionItems()) { 1166 actionRequest.getSimulatedActionItems().add((ActionItem) ObjectUtils.deepCopy(actionItem)); 1167 } 1168 copiedRequests.add(actionRequest); 1169 } 1170 1171 ActivationContext activationContext = new ActivationContext(ActivationContext.CONTEXT_IS_SIMULATION); 1172 for (ActionRequestValue request : copiedRequests) { 1173 if (activateFirst.booleanValue() && !request.isActive()) { 1174 KEWServiceLocator.getActionRequestService().activateRequest(request, activationContext); 1175 } 1176 if (request.isUserRequest() && request.getPrincipalId().equals(principalId)) { 1177 KEWServiceLocator.getActionRequestService().deactivateRequest(null, request, activationContext); 1178 } else if (request.isGroupRequest() && KimApiServiceLocator.getGroupService().isMemberOfGroup(principalId, request.getGroup().getId())) { 1179 KEWServiceLocator.getActionRequestService().deactivateRequest(null, request, activationContext); 1180 } 1181 } 1182 boolean allDeactivated = true; 1183 for (ActionRequestValue actionRequest: copiedRequests) { 1184 allDeactivated = allDeactivated && actionRequest.isDeactivated(); 1185 } 1186 return allDeactivated; 1187 } 1188 1189 @Override 1190 public boolean isUserInRouteLog(String documentId, String principalId, boolean lookFuture) { 1191 incomingParamCheck(documentId, "documentId"); 1192 incomingParamCheck(principalId, "principalId"); 1193 return isUserInRouteLogWithOptionalFlattening(documentId, principalId, lookFuture, false); 1194 } 1195 1196 @Override 1197 public boolean isUserInRouteLogWithOptionalFlattening(String documentId, String principalId, boolean lookFuture, boolean flattenNodes) { 1198 incomingParamCheck(documentId, "documentId"); 1199 incomingParamCheck(principalId, "principalId"); 1200 boolean authorized = false; 1201 if ( LOG.isDebugEnabled() ) { 1202 LOG.debug("Evaluating isUserInRouteLog [docId=" + documentId + ", principalId=" + principalId + ", lookFuture=" + lookFuture + "]"); 1203 } 1204 DocumentRouteHeaderValue routeHeader = loadDocument(documentId); 1205 if (routeHeader == null) { 1206 throw new IllegalArgumentException("Document for documentId: " + documentId + " does not exist"); 1207 } 1208 Principal principal = KEWServiceLocator.getIdentityHelperService().getPrincipal(principalId); 1209 if (principal == null) { 1210 throw new IllegalArgumentException("Principal for principalId: " + principalId + " does not exist"); 1211 } 1212 List<ActionTakenValue> actionsTaken = KEWServiceLocator.getActionTakenService().findByDocumentIdWorkflowId(documentId, principal.getPrincipalId()); 1213 1214 if(routeHeader.getInitiatorWorkflowId().equals(principal.getPrincipalId())){ 1215 return true; 1216 } 1217 1218 if (!actionsTaken.isEmpty()) { 1219 LOG.debug("found action taken by user"); 1220 authorized = true; 1221 } 1222 1223 List<ActionRequestValue> actionRequests = KEWServiceLocator.getActionRequestService().findAllActionRequestsByDocumentId(documentId); 1224 if (actionRequestListHasPrincipal(principal, actionRequests)) { 1225 authorized = true; 1226 } 1227 1228 if (!lookFuture || authorized) { 1229 return authorized; 1230 } 1231 1232 1233 SimulationWorkflowEngine simulationEngine = KEWServiceLocator.getSimulationEngine(); 1234 SimulationCriteria criteria = SimulationCriteria.createSimulationCritUsingDocumentId(documentId); 1235 criteria.setDestinationNodeName(null); // process entire document to conclusion 1236 criteria.getDestinationRecipients().add(new KimPrincipalRecipient(principal)); 1237 criteria.setFlattenNodes(flattenNodes); 1238 1239 try { 1240 SimulationResults results = simulationEngine.runSimulation(criteria); 1241 if (actionRequestListHasPrincipal(principal, results.getSimulatedActionRequests())) { 1242 authorized = true; 1243 } 1244 } catch (Exception e) { 1245 throw new RiceRuntimeException(e); 1246 } 1247 1248 return authorized; 1249 } 1250 1251 private boolean actionRequestListHasPrincipal(Principal principal, List<ActionRequestValue> actionRequests) { 1252 for (ActionRequestValue actionRequest : actionRequests) { 1253 if (actionRequest.isRecipientRoutedRequest(new KimPrincipalRecipient(principal))) { 1254 return true; 1255 } 1256 } 1257 return false; 1258 } 1259 1260 public List<String> getPrincipalIdsInRouteLog(String documentId, boolean lookFuture) { 1261 if (StringUtils.isEmpty(documentId)) { 1262 throw new IllegalArgumentException("documentId passed in is null or blank"); 1263 } 1264 Set<String> principalIds = new HashSet<String>(); 1265 try { 1266 if ( LOG.isDebugEnabled() ) { 1267 LOG.debug("Evaluating isUserInRouteLog [docId=" + documentId + ", lookFuture=" + lookFuture + "]"); 1268 } 1269 DocumentRouteHeaderValue routeHeader = loadDocument(documentId); 1270 List<ActionTakenValue> actionsTakens = 1271 (List<ActionTakenValue>)KEWServiceLocator.getActionTakenService().findByDocumentId(documentId); 1272 //TODO: confirm that the initiator is not already there in the actionstaken 1273 principalIds.add(routeHeader.getInitiatorWorkflowId()); 1274 for(ActionTakenValue actionTaken: actionsTakens){ 1275 principalIds.add(actionTaken.getPrincipalId()); 1276 } 1277 List<ActionRequestValue> actionRequests = 1278 KEWServiceLocator.getActionRequestService().findAllActionRequestsByDocumentId(documentId); 1279 for(ActionRequestValue actionRequest: actionRequests){ 1280 principalIds.addAll(getPrincipalIdsForActionRequest(actionRequest)); 1281 } 1282 if (!lookFuture) { 1283 return new ArrayList<String>(principalIds); 1284 } 1285 SimulationWorkflowEngine simulationEngine = KEWServiceLocator.getSimulationEngine(); 1286 SimulationCriteria criteria = SimulationCriteria.createSimulationCritUsingDocumentId(documentId); 1287 criteria.setDestinationNodeName(null); // process entire document to conclusion 1288 SimulationResults results = simulationEngine.runSimulation(criteria); 1289 actionRequests = (List<ActionRequestValue>)results.getSimulatedActionRequests(); 1290 for(ActionRequestValue actionRequest: actionRequests){ 1291 principalIds.addAll(getPrincipalIdsForActionRequest(actionRequest)); 1292 } 1293 } catch (Exception ex) { 1294 LOG.warn("Problems getting principalIds in Route Log for documentId: "+documentId+". Exception:"+ex.getMessage(),ex); 1295 } 1296 return new ArrayList<String>(principalIds); 1297 } 1298 1299 private DocumentRouteHeaderValue loadDocument(String documentId) { 1300 return KEWServiceLocator.getRouteHeaderService().getRouteHeader(documentId); 1301 } 1302 1303 /** 1304 * This method gets all of the principalIds for the given ActionRequestValue. It drills down into 1305 * groups if need be. 1306 * 1307 * @param actionRequest 1308 */ 1309 private List<String> getPrincipalIdsForActionRequest(ActionRequestValue actionRequest) { 1310 List<String> results = Collections.emptyList(); 1311 if (actionRequest.getPrincipalId() != null) { 1312 results = Collections.singletonList(actionRequest.getPrincipalId()); 1313 } else if (actionRequest.getGroupId() != null) { 1314 List<String> principalIdsForGroup = 1315 KimApiServiceLocator.getGroupService().getMemberPrincipalIds(actionRequest.getGroupId()); 1316 if (principalIdsForGroup != null) { 1317 results = principalIdsForGroup; 1318 } 1319 } 1320 return results; 1321 } 1322 1323 private void incomingParamCheck(Object object, String name) { 1324 if (object == null) { 1325 throw new RiceIllegalArgumentException(name + " was null"); 1326 } else if (object instanceof String 1327 && StringUtils.isBlank((String) object)) { 1328 throw new RiceIllegalArgumentException(name + " was blank"); 1329 } 1330 } 1331 1332 public void setDocumentTypeService(DocumentTypeService documentTypeService) { 1333 this.documentTypeService = documentTypeService; 1334 } 1335 1336 /** 1337 * TODO - this code is temporary until we get rid of all the crazy throwing of 1338 * "WorkflowException" 1339 */ 1340 private void translateException(WorkflowException e) { 1341 if (e instanceof org.kuali.rice.kew.api.exception.InvalidActionTakenException) { 1342 throw new InvalidActionTakenException(e.getMessage(), e); 1343 } 1344 throw new WorkflowRuntimeException(e.getMessage(), e); 1345 } 1346 1347 protected DocumentActionResult executeActionInternal(DocumentActionParameters parameters, 1348 DocumentActionCallback callback) { 1349 if (parameters == null) { 1350 throw new RiceIllegalArgumentException("Document action parameters was null."); 1351 } 1352 if (LOG.isDebugEnabled()) { 1353 LOG.debug(callback.getLogMessage(parameters.getDocumentId(), parameters.getPrincipalId(), 1354 parameters.getAnnotation())); 1355 } 1356 DocumentRouteHeaderValue documentBo = init(parameters); 1357 try { 1358 documentBo = callback.doInDocumentBo(documentBo, parameters.getPrincipalId(), parameters.getAnnotation()); 1359 } catch (WorkflowException e) { 1360 // TODO fix this up once the checked exception goes away 1361 translateException(e); 1362 } 1363 return constructDocumentActionResult(documentBo, parameters.getPrincipalId()); 1364 } 1365 1366 protected static interface DocumentActionCallback { 1367 1368 DocumentRouteHeaderValue doInDocumentBo(DocumentRouteHeaderValue documentBo, String principalId, 1369 String annotation) throws WorkflowException; 1370 1371 String getLogMessage(String documentId, String principalId, String annotation); 1372 1373 } 1374 1375 protected static abstract class StandardDocumentActionCallback implements DocumentActionCallback { 1376 1377 public final String getLogMessage(String documentId, String principalId, String annotation) { 1378 return getActionName() + " [principalId=" + principalId + ", documentId=" + documentId + ", annotation=" 1379 + annotation + "]"; 1380 } 1381 1382 protected abstract String getActionName(); 1383 1384 } 1385 1386}