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