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