001 /** 002 * Copyright 2005-2012 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.kew.impl.document; 017 018 import java.io.Serializable; 019 import java.util.Arrays; 020 import java.util.Collections; 021 import java.util.Date; 022 import java.util.HashSet; 023 import java.util.List; 024 import java.util.Map; 025 import java.util.Set; 026 027 import org.apache.commons.lang.StringUtils; 028 import org.joda.time.DateTime; 029 import org.kuali.rice.core.api.uif.RemotableAttributeErrorContract; 030 import org.kuali.rice.kew.api.KewApiConstants; 031 import org.kuali.rice.kew.api.KewApiServiceLocator; 032 import org.kuali.rice.kew.api.action.ActionRequest; 033 import org.kuali.rice.kew.api.action.ActionRequestType; 034 import org.kuali.rice.kew.api.action.ActionTaken; 035 import org.kuali.rice.kew.api.action.ActionType; 036 import org.kuali.rice.kew.api.action.AdHocRevoke; 037 import org.kuali.rice.kew.api.action.AdHocToGroup; 038 import org.kuali.rice.kew.api.action.AdHocToPrincipal; 039 import org.kuali.rice.kew.api.action.DocumentActionParameters; 040 import org.kuali.rice.kew.api.action.DocumentActionResult; 041 import org.kuali.rice.kew.api.action.MovePoint; 042 import org.kuali.rice.kew.api.action.RequestedActions; 043 import org.kuali.rice.kew.api.action.ReturnPoint; 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.document.Document; 047 import org.kuali.rice.kew.api.document.DocumentContent; 048 import org.kuali.rice.kew.api.document.DocumentContentUpdate; 049 import org.kuali.rice.kew.api.document.DocumentDetail; 050 import org.kuali.rice.kew.api.document.DocumentStatus; 051 import org.kuali.rice.kew.api.document.DocumentUpdate; 052 import org.kuali.rice.kew.api.document.attribute.WorkflowAttributeDefinition; 053 import org.kuali.rice.kew.api.document.node.RouteNodeInstance; 054 import org.kuali.rice.kew.api.document.WorkflowDocumentService; 055 056 /** 057 * The implementation of {@link org.kuali.rice.kew.api.WorkflowDocument}. Implements {@link WorkflowDocumentPrototype} to expose 058 * and initialization method used for construction. 059 * <p>NOTE: operations against document data on this are only "flushed" when an action is performed.</p> 060 * <p><b>This class is *not* thread safe.</b></p> 061 * @see org.kuali.rice.kew.api.WorkflowDocument 062 */ 063 public class WorkflowDocumentImpl implements Serializable, WorkflowDocumentPrototype { 064 065 private static final long serialVersionUID = -3672966990721719088L; 066 067 /** 068 * The principal id under which all document actions will be performed. 069 */ 070 private String principalId; 071 /** 072 * Stores local changes that need to be committed. 073 */ 074 private ModifiableDocument modifiableDocument; 075 /** 076 * Stores local changes that need to be committed. 077 */ 078 private ModifiableDocumentContent modifiableDocumentContent; 079 /** 080 * Local cache of valid document actions. 081 * @see #getValidActions() 082 */ 083 private ValidActions validActions; 084 /** 085 * Local cache of requested document actions. 086 * @see #getRequestedActions() 087 */ 088 private RequestedActions requestedActions; 089 /** 090 * Flag that indicates whether the document has been deleted; if so the object is thereafter in an illegal state. 091 */ 092 private boolean documentDeleted = false; 093 094 private transient WorkflowDocumentActionsService workflowDocumentActionsService; 095 private transient WorkflowDocumentService workflowDocumentService; 096 097 public void init(String principalId, Document document) { 098 if (StringUtils.isBlank("principalId")) { 099 throw new IllegalArgumentException("principalId was null or blank"); 100 } 101 if (document == null) { 102 throw new IllegalArgumentException("document was null"); 103 } 104 this.principalId = principalId; 105 this.modifiableDocument = new ModifiableDocument(document); 106 this.modifiableDocumentContent = null; 107 this.validActions = null; 108 this.requestedActions = null; 109 } 110 111 public WorkflowDocumentActionsService getWorkflowDocumentActionsService() { 112 if (workflowDocumentActionsService == null) { 113 workflowDocumentActionsService = KewApiServiceLocator.getWorkflowDocumentActionsService(); 114 } 115 return workflowDocumentActionsService; 116 } 117 118 public void setWorkflowDocumentActionsService(WorkflowDocumentActionsService workflowDocumentActionsService) { 119 this.workflowDocumentActionsService = workflowDocumentActionsService; 120 } 121 122 public WorkflowDocumentService getWorkflowDocumentService() { 123 if (workflowDocumentService == null) { 124 workflowDocumentService = KewApiServiceLocator.getWorkflowDocumentService(); 125 } 126 return workflowDocumentService; 127 } 128 129 public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) { 130 this.workflowDocumentService = workflowDocumentService; 131 } 132 133 protected ModifiableDocument getModifiableDocument() { 134 return modifiableDocument; 135 } 136 137 protected ModifiableDocumentContent getModifiableDocumentContent() { 138 if (this.modifiableDocumentContent == null) { 139 DocumentContent documentContent = getWorkflowDocumentService().getDocumentContent(getDocumentId()); 140 if (documentContent == null) { 141 throw new IllegalStateException("Failed to load document content for documentId: " + getDocumentId()); 142 } 143 this.modifiableDocumentContent = new ModifiableDocumentContent(documentContent); 144 } 145 return this.modifiableDocumentContent; 146 } 147 148 @Override 149 public String getDocumentId() { 150 if (documentDeleted) { 151 throw new IllegalStateException("Document has been deleted."); 152 } 153 return getModifiableDocument().getDocumentId(); 154 } 155 156 @Override 157 public Document getDocument() { 158 return getModifiableDocument().getDocument(); 159 } 160 161 @Override 162 public DocumentContent getDocumentContent() { 163 return getModifiableDocumentContent().getDocumentContent(); 164 } 165 166 @Override 167 public String getApplicationContent() { 168 return getDocumentContent().getApplicationContent(); 169 } 170 171 @Override 172 public void setApplicationContent(String applicationContent) { 173 getModifiableDocumentContent().setApplicationContent(applicationContent); 174 } 175 176 @Override 177 public void setAttributeContent(String attributeContent) { 178 getModifiableDocumentContent().setAttributeContent(attributeContent); 179 } 180 181 @Override 182 public void clearAttributeContent() { 183 getModifiableDocumentContent().setAttributeContent(""); 184 } 185 186 @Override 187 public String getAttributeContent() { 188 return getDocumentContent().getAttributeContent(); 189 } 190 191 @Override 192 public void addAttributeDefinition(WorkflowAttributeDefinition attributeDefinition) { 193 getModifiableDocumentContent().addAttributeDefinition(attributeDefinition); 194 } 195 196 @Override 197 public void removeAttributeDefinition(WorkflowAttributeDefinition attributeDefinition) { 198 getModifiableDocumentContent().removeAttributeDefinition(attributeDefinition); 199 } 200 201 @Override 202 public void clearAttributeDefinitions() { 203 getAttributeDefinitions().clear(); 204 } 205 206 @Override 207 public List<WorkflowAttributeDefinition> getAttributeDefinitions() { 208 return getModifiableDocumentContent().getAttributeDefinitions(); 209 } 210 211 @Override 212 public void setSearchableContent(String searchableContent) { 213 getModifiableDocumentContent().setSearchableContent(searchableContent); 214 } 215 216 @Override 217 public void addSearchableDefinition(WorkflowAttributeDefinition searchableDefinition) { 218 getModifiableDocumentContent().addSearchableDefinition(searchableDefinition); 219 } 220 221 @Override 222 public void removeSearchableDefinition(WorkflowAttributeDefinition searchableDefinition) { 223 getModifiableDocumentContent().removeSearchableDefinition(searchableDefinition); 224 } 225 226 @Override 227 public void clearSearchableDefinitions() { 228 getSearchableDefinitions().clear(); 229 } 230 231 @Override 232 public void clearSearchableContent() { 233 getModifiableDocumentContent().setSearchableContent(""); 234 } 235 236 @Override 237 public List<WorkflowAttributeDefinition> getSearchableDefinitions() { 238 return getModifiableDocumentContent().getSearchableDefinitions(); 239 } 240 241 @Override 242 public List<? extends RemotableAttributeErrorContract> validateAttributeDefinition( 243 WorkflowAttributeDefinition attributeDefinition) { 244 return getWorkflowDocumentActionsService().validateWorkflowAttributeDefinition(attributeDefinition); 245 } 246 247 @Override 248 public List<ActionRequest> getRootActionRequests() { 249 return getWorkflowDocumentService().getRootActionRequests(getDocumentId()); 250 } 251 252 @Override 253 public List<ActionTaken> getActionsTaken() { 254 return getWorkflowDocumentService().getActionsTaken(getDocumentId()); 255 } 256 257 @Override 258 public void setApplicationDocumentId(String applicationDocumentId) { 259 getModifiableDocument().setApplicationDocumentId(applicationDocumentId); 260 } 261 262 @Override 263 public String getApplicationDocumentId() { 264 return getModifiableDocument().getApplicationDocumentId(); 265 } 266 267 @Override 268 public DateTime getDateCreated() { 269 return getModifiableDocument().getDateCreated(); 270 } 271 272 @Override 273 public String getTitle() { 274 return getModifiableDocument().getTitle(); 275 } 276 277 @Override 278 public ValidActions getValidActions() { 279 if (validActions == null) { 280 validActions = getWorkflowDocumentActionsService().determineValidActions(getDocumentId(), getPrincipalId()); 281 } 282 return validActions; 283 } 284 285 @Override 286 public RequestedActions getRequestedActions() { 287 if (requestedActions == null) { 288 requestedActions = getWorkflowDocumentActionsService().determineRequestedActions(getDocumentId(), 289 getPrincipalId()); 290 } 291 return requestedActions; 292 } 293 294 protected DocumentUpdate getDocumentUpdateIfDirty() { 295 if (getModifiableDocument().isDirty()) { 296 return getModifiableDocument().build(); 297 } 298 return null; 299 } 300 301 protected DocumentContentUpdate getDocumentContentUpdateIfDirty() { 302 if (getModifiableDocumentContent().isDirty()) { 303 return getModifiableDocumentContent().build(); 304 } 305 return null; 306 } 307 308 protected void resetStateAfterAction(DocumentActionResult response) { 309 this.modifiableDocument = new ModifiableDocument(response.getDocument()); 310 this.validActions = null; 311 if (response.getValidActions() != null) { 312 this.validActions = response.getValidActions(); 313 } 314 this.requestedActions = null; 315 if (response.getRequestedActions() != null) { 316 this.requestedActions = response.getRequestedActions(); 317 } 318 // regardless of whether modifiable document content is dirty, we null it out so it will be re-fetched next time it's needed 319 this.modifiableDocumentContent = null; 320 } 321 322 @Override 323 public void saveDocument(String annotation) { 324 DocumentActionResult result = getWorkflowDocumentActionsService().save( 325 constructDocumentActionParameters(annotation)); 326 resetStateAfterAction(result); 327 } 328 329 @Override 330 public void route(String annotation) { 331 DocumentActionResult result = getWorkflowDocumentActionsService().route( 332 constructDocumentActionParameters(annotation)); 333 resetStateAfterAction(result); 334 } 335 336 @Override 337 public void disapprove(String annotation) { 338 DocumentActionResult result = getWorkflowDocumentActionsService().disapprove( 339 constructDocumentActionParameters(annotation)); 340 resetStateAfterAction(result); 341 } 342 343 @Override 344 public void approve(String annotation) { 345 DocumentActionResult result = getWorkflowDocumentActionsService().approve( 346 constructDocumentActionParameters(annotation)); 347 resetStateAfterAction(result); 348 } 349 350 @Override 351 public void cancel(String annotation) { 352 DocumentActionResult result = getWorkflowDocumentActionsService().cancel( 353 constructDocumentActionParameters(annotation)); 354 resetStateAfterAction(result); 355 } 356 357 @Override 358 public void blanketApprove(String annotation) { 359 DocumentActionResult result = getWorkflowDocumentActionsService().blanketApprove( 360 constructDocumentActionParameters(annotation)); 361 resetStateAfterAction(result); 362 } 363 364 @Override 365 public void blanketApprove(String annotation, String... nodeNames) { 366 if (nodeNames == null) { 367 throw new IllegalArgumentException("nodeNames was null"); 368 } 369 Set<String> nodeNamesSet = new HashSet<String>(Arrays.asList(nodeNames)); 370 DocumentActionResult result = getWorkflowDocumentActionsService().blanketApproveToNodes( 371 constructDocumentActionParameters(annotation), nodeNamesSet); 372 resetStateAfterAction(result); 373 } 374 375 @Override 376 public void saveDocumentData() { 377 DocumentActionResult result = getWorkflowDocumentActionsService().saveDocumentData( 378 constructDocumentActionParameters(null)); 379 resetStateAfterAction(result); 380 } 381 382 @Override 383 public void setApplicationDocumentStatus(String applicationDocumentStatus) { 384 getModifiableDocument().setApplicationDocumentStatus(applicationDocumentStatus); 385 } 386 387 @Override 388 public void acknowledge(String annotation) { 389 DocumentActionResult result = getWorkflowDocumentActionsService().acknowledge( 390 constructDocumentActionParameters(annotation)); 391 resetStateAfterAction(result); 392 } 393 394 @Override 395 public void fyi(String annotation) { 396 DocumentActionResult result = getWorkflowDocumentActionsService().clearFyi( 397 constructDocumentActionParameters(annotation)); 398 resetStateAfterAction(result); 399 } 400 401 @Override 402 public void fyi() { 403 fyi(""); 404 } 405 406 @Override 407 public void delete() { 408 getWorkflowDocumentActionsService().delete(getDocumentId(), principalId); 409 documentDeleted = true; 410 } 411 412 @Override 413 public void refresh() { 414 Document document = getWorkflowDocumentService().getDocument(getDocumentId()); 415 this.modifiableDocument = new ModifiableDocument(document); 416 this.validActions = null; 417 this.requestedActions = null; 418 this.modifiableDocumentContent = null; 419 } 420 421 @Override 422 public void adHocToPrincipal(ActionRequestType actionRequested, String annotation, String targetPrincipalId, 423 String responsibilityDescription, boolean forceAction) { 424 adHocToPrincipal(actionRequested, null, annotation, targetPrincipalId, responsibilityDescription, forceAction); 425 } 426 427 @Override 428 public void adHocToPrincipal(ActionRequestType actionRequested, String nodeName, String annotation, 429 String targetPrincipalId, String responsibilityDescription, boolean forceAction) { 430 adHocToPrincipal(actionRequested, nodeName, annotation, targetPrincipalId, responsibilityDescription, 431 forceAction, null); 432 } 433 434 @Override 435 public void adHocToPrincipal(ActionRequestType actionRequested, String nodeName, String annotation, 436 String targetPrincipalId, String responsibilityDescription, boolean forceAction, String requestLabel) { 437 AdHocToPrincipal.Builder builder = AdHocToPrincipal.Builder 438 .create(actionRequested, nodeName, targetPrincipalId); 439 builder.setResponsibilityDescription(responsibilityDescription); 440 builder.setForceAction(forceAction); 441 builder.setRequestLabel(requestLabel); 442 DocumentActionResult result = getWorkflowDocumentActionsService().adHocToPrincipal( 443 constructDocumentActionParameters(annotation), builder.build()); 444 resetStateAfterAction(result); 445 } 446 447 @Override 448 public void adHocToPrincipal(AdHocToPrincipal adHocToPrincipal, String annotation) { 449 DocumentActionResult result = getWorkflowDocumentActionsService().adHocToPrincipal( 450 constructDocumentActionParameters(annotation), adHocToPrincipal); 451 resetStateAfterAction(result); 452 } 453 454 @Override 455 public void adHocToGroup(ActionRequestType actionRequested, String annotation, String targetGroupId, 456 String responsibilityDescription, boolean forceAction) { 457 adHocToGroup(actionRequested, null, annotation, targetGroupId, responsibilityDescription, forceAction); 458 } 459 460 @Override 461 public void adHocToGroup(ActionRequestType actionRequested, String nodeName, String annotation, 462 String targetGroupId, String responsibilityDescription, boolean forceAction) { 463 adHocToGroup(actionRequested, nodeName, annotation, targetGroupId, responsibilityDescription, forceAction, null); 464 } 465 466 @Override 467 public void adHocToGroup(ActionRequestType actionRequested, String nodeName, String annotation, 468 String targetGroupId, String responsibilityDescription, boolean forceAction, String requestLabel) { 469 AdHocToGroup.Builder builder = AdHocToGroup.Builder.create(actionRequested, nodeName, targetGroupId); 470 builder.setResponsibilityDescription(responsibilityDescription); 471 builder.setForceAction(forceAction); 472 builder.setRequestLabel(requestLabel); 473 DocumentActionResult result = getWorkflowDocumentActionsService().adHocToGroup( 474 constructDocumentActionParameters(annotation), builder.build()); 475 resetStateAfterAction(result); 476 } 477 478 @Override 479 public void adHocToGroup(AdHocToGroup adHocToGroup, String annotation) { 480 DocumentActionResult result = getWorkflowDocumentActionsService().adHocToGroup( 481 constructDocumentActionParameters(annotation), adHocToGroup); 482 resetStateAfterAction(result); 483 } 484 485 @Override 486 public void revokeAdHocRequestById(String actionRequestId, String annotation) { 487 if (StringUtils.isBlank(actionRequestId)) { 488 throw new IllegalArgumentException("actionRequestId was null or blank"); 489 } 490 DocumentActionResult result = getWorkflowDocumentActionsService().revokeAdHocRequestById( 491 constructDocumentActionParameters(annotation), actionRequestId); 492 resetStateAfterAction(result); 493 } 494 495 @Override 496 public void revokeAdHocRequests(AdHocRevoke revoke, String annotation) { 497 if (revoke == null) { 498 throw new IllegalArgumentException("revokeFromPrincipal was null"); 499 } 500 DocumentActionResult result = getWorkflowDocumentActionsService().revokeAdHocRequests( 501 constructDocumentActionParameters(annotation), revoke); 502 resetStateAfterAction(result); 503 } 504 505 @Override 506 public void revokeAllAdHocRequests(String annotation) { 507 DocumentActionResult result = getWorkflowDocumentActionsService().revokeAllAdHocRequests( 508 constructDocumentActionParameters(annotation)); 509 resetStateAfterAction(result); 510 } 511 512 @Override 513 public void setTitle(String title) { 514 getModifiableDocument().setTitle(title); 515 } 516 517 @Override 518 public String getDocumentTypeName() { 519 return getDocument().getDocumentTypeName(); 520 } 521 522 @Override 523 public boolean isCompletionRequested() { 524 return getRequestedActions().isCompleteRequested(); 525 } 526 527 @Override 528 public boolean isApprovalRequested() { 529 return getRequestedActions().isApproveRequested(); 530 } 531 532 @Override 533 public boolean isAcknowledgeRequested() { 534 return getRequestedActions().isAcknowledgeRequested(); 535 } 536 537 @Override 538 public boolean isFYIRequested() { 539 return getRequestedActions().isFyiRequested(); 540 } 541 542 @Override 543 public boolean isBlanketApproveCapable() { 544 return isValidAction(ActionType.BLANKET_APPROVE) 545 && (isCompletionRequested() || isApprovalRequested() || isInitiated()); 546 } 547 548 @Override 549 public boolean isRouteCapable() { 550 return isValidAction(ActionType.ROUTE); 551 } 552 553 @Override 554 public boolean isValidAction(ActionType actionType) { 555 if (actionType == null) { 556 throw new IllegalArgumentException("actionType was null"); 557 } 558 return getValidActions().getValidActions().contains(actionType); 559 } 560 561 @Override 562 public void superUserBlanketApprove(String annotation) { 563 DocumentActionResult result = getWorkflowDocumentActionsService().superUserBlanketApprove( 564 constructDocumentActionParameters(annotation), true); 565 resetStateAfterAction(result); 566 } 567 568 @Override 569 public void superUserNodeApprove(String nodeName, String annotation) { 570 DocumentActionResult result = getWorkflowDocumentActionsService().superUserNodeApprove( 571 constructDocumentActionParameters(annotation), true, nodeName); 572 resetStateAfterAction(result); 573 } 574 575 @Override 576 public void superUserTakeRequestedAction(String actionRequestId, String annotation) { 577 DocumentActionResult result = getWorkflowDocumentActionsService().superUserTakeRequestedAction( 578 constructDocumentActionParameters(annotation), true, actionRequestId); 579 resetStateAfterAction(result); 580 } 581 582 @Override 583 public void superUserDisapprove(String annotation) { 584 DocumentActionResult result = getWorkflowDocumentActionsService().superUserDisapprove( 585 constructDocumentActionParameters(annotation), true); 586 resetStateAfterAction(result); 587 } 588 589 @Override 590 public void superUserCancel(String annotation) { 591 DocumentActionResult result = getWorkflowDocumentActionsService().superUserCancel( 592 constructDocumentActionParameters(annotation), true); 593 resetStateAfterAction(result); 594 } 595 596 @Override 597 public void superUserReturnToPreviousNode(ReturnPoint returnPoint, String annotation) { 598 DocumentActionResult result = getWorkflowDocumentActionsService().superUserReturnToPreviousNode( 599 constructDocumentActionParameters(annotation), true, returnPoint); 600 resetStateAfterAction(result); 601 } 602 603 @Override 604 public void complete(String annotation) { 605 DocumentActionResult result = getWorkflowDocumentActionsService().complete( 606 constructDocumentActionParameters(annotation)); 607 resetStateAfterAction(result); 608 } 609 610 @Override 611 public void logAnnotation(String annotation) { 612 getWorkflowDocumentActionsService().logAnnotation(getDocumentId(), principalId, annotation); 613 } 614 615 @Override 616 public DocumentStatus getStatus() { 617 return getDocument().getStatus(); 618 } 619 620 @Override 621 public boolean checkStatus(DocumentStatus status) { 622 if (status == null) { 623 throw new IllegalArgumentException("status was null"); 624 } 625 return status == getStatus(); 626 } 627 628 /** 629 * Indicates if the document is in the initiated state or not. 630 * 631 * @return true if in the specified state 632 */ 633 @Override 634 public boolean isInitiated() { 635 return checkStatus(DocumentStatus.INITIATED); 636 } 637 638 /** 639 * Indicates if the document is in the saved state or not. 640 * 641 * @return true if in the specified state 642 */ 643 @Override 644 public boolean isSaved() { 645 return checkStatus(DocumentStatus.SAVED); 646 } 647 648 /** 649 * Indicates if the document is in the enroute state or not. 650 * 651 * @return true if in the specified state 652 */ 653 @Override 654 public boolean isEnroute() { 655 return checkStatus(DocumentStatus.ENROUTE); 656 } 657 658 /** 659 * Indicates if the document is in the exception state or not. 660 * 661 * @return true if in the specified state 662 */ 663 @Override 664 public boolean isException() { 665 return checkStatus(DocumentStatus.EXCEPTION); 666 } 667 668 /** 669 * Indicates if the document is in the canceled state or not. 670 * 671 * @return true if in the specified state 672 */ 673 @Override 674 public boolean isCanceled() { 675 return checkStatus(DocumentStatus.CANCELED); 676 } 677 678 /** 679 * Indicates if the document is in the disapproved state or not. 680 * 681 * @return true if in the specified state 682 */ 683 @Override 684 public boolean isDisapproved() { 685 return checkStatus(DocumentStatus.DISAPPROVED); 686 } 687 688 /** 689 * Indicates if the document is in the Processed or Finalized state. 690 * 691 * @return true if in the specified state 692 */ 693 @Override 694 public boolean isApproved() { 695 return isProcessed() || isFinal(); 696 } 697 698 /** 699 * Indicates if the document is in the processed state or not. 700 * 701 * @return true if in the specified state 702 */ 703 @Override 704 public boolean isProcessed() { 705 return checkStatus(DocumentStatus.PROCESSED); 706 } 707 708 /** 709 * Indicates if the document is in the final state or not. 710 * 711 * @return true if in the specified state 712 */ 713 @Override 714 public boolean isFinal() { 715 return checkStatus(DocumentStatus.FINAL); 716 } 717 718 /** 719 * Returns the principalId with which this WorkflowDocument was constructed 720 * 721 * @return the principalId with which this WorkflowDocument was constructed 722 */ 723 @Override 724 public String getPrincipalId() { 725 return principalId; 726 } 727 728 @Override 729 public void switchPrincipal(String principalId) { 730 if (StringUtils.isBlank(this.principalId)) { 731 throw new IllegalArgumentException("principalId was null or blank"); 732 } 733 this.principalId = principalId; 734 this.validActions = null; 735 this.requestedActions = null; 736 } 737 738 @Override 739 public void takeGroupAuthority(String annotation, String groupId) { 740 DocumentActionResult result = getWorkflowDocumentActionsService().takeGroupAuthority( 741 constructDocumentActionParameters(annotation), groupId); 742 resetStateAfterAction(result); 743 } 744 745 @Override 746 public void releaseGroupAuthority(String annotation, String groupId) { 747 DocumentActionResult result = getWorkflowDocumentActionsService().releaseGroupAuthority( 748 constructDocumentActionParameters(annotation), groupId); 749 resetStateAfterAction(result); 750 } 751 752 @Override 753 public Set<String> getNodeNames() { 754 List<RouteNodeInstance> activeNodeInstances = getActiveRouteNodeInstances(); 755 Set<String> nodeNames = new HashSet<String>(activeNodeInstances.size()); 756 for (RouteNodeInstance routeNodeInstance : activeNodeInstances) { 757 nodeNames.add(routeNodeInstance.getName()); 758 } 759 return Collections.unmodifiableSet(nodeNames); 760 } 761 762 public Set<String> getCurrentNodeNames() { 763 List<RouteNodeInstance> currentNodeInstances = getCurrentRouteNodeInstances(); 764 Set<String> nodeNames = new HashSet<String>(currentNodeInstances.size()); 765 for (RouteNodeInstance routeNodeInstance : currentNodeInstances) { 766 nodeNames.add(routeNodeInstance.getName()); 767 } 768 return Collections.unmodifiableSet(nodeNames); 769 } 770 771 @Override 772 public void returnToPreviousNode(String annotation, String nodeName) { 773 if (nodeName == null) { 774 throw new IllegalArgumentException("nodeName was null"); 775 } 776 returnToPreviousNode(annotation, ReturnPoint.create(nodeName)); 777 } 778 779 @Override 780 public void returnToPreviousNode(String annotation, ReturnPoint returnPoint) { 781 if (returnPoint == null) { 782 throw new IllegalArgumentException("returnPoint was null"); 783 } 784 DocumentActionResult result = getWorkflowDocumentActionsService().returnToPreviousNode( 785 constructDocumentActionParameters(annotation), returnPoint); 786 resetStateAfterAction(result); 787 } 788 789 @Override 790 public void move(MovePoint movePoint, String annotation) { 791 if (movePoint == null) { 792 throw new IllegalArgumentException("movePoint was null"); 793 } 794 DocumentActionResult result = getWorkflowDocumentActionsService().move( 795 constructDocumentActionParameters(annotation), movePoint); 796 resetStateAfterAction(result); 797 } 798 799 @Override 800 public List<RouteNodeInstance> getActiveRouteNodeInstances() { 801 return getWorkflowDocumentService().getActiveRouteNodeInstances(getDocumentId()); 802 } 803 804 @Override 805 public List<RouteNodeInstance> getCurrentRouteNodeInstances() { 806 return getWorkflowDocumentService().getCurrentRouteNodeInstances(getDocumentId()); 807 } 808 809 @Override 810 public List<RouteNodeInstance> getRouteNodeInstances() { 811 return getWorkflowDocumentService().getRouteNodeInstances(getDocumentId()); 812 } 813 814 @Override 815 public List<String> getPreviousNodeNames() { 816 return getWorkflowDocumentService().getPreviousRouteNodeNames(getDocumentId()); 817 } 818 819 @Override 820 public DocumentDetail getDocumentDetail() { 821 return getWorkflowDocumentService().getDocumentDetail(getDocumentId()); 822 } 823 824 @Override 825 public void updateDocumentContent(DocumentContentUpdate documentContentUpdate) { 826 if (documentContentUpdate == null) { 827 throw new IllegalArgumentException("documentContentUpdate was null."); 828 } 829 getModifiableDocumentContent().setDocumentContentUpdate(documentContentUpdate); 830 } 831 832 @Override 833 public void placeInExceptionRouting(String annotation) { 834 DocumentActionResult result = getWorkflowDocumentActionsService().placeInExceptionRouting( 835 constructDocumentActionParameters(annotation)); 836 resetStateAfterAction(result); 837 } 838 839 @Override 840 public void setVariable(String name, String value) { 841 getModifiableDocument().setVariable(name, value); 842 } 843 844 @Override 845 public String getVariableValue(String name) { 846 return getModifiableDocument().getVariableValue(name); 847 } 848 849 @Override 850 public void setReceiveFutureRequests() { 851 setVariable(getFutureRequestsKey(principalId), getReceiveFutureRequestsValue()); 852 } 853 854 @Override 855 public void setDoNotReceiveFutureRequests() { 856 this.setVariable(getFutureRequestsKey(principalId), getDoNotReceiveFutureRequestsValue()); 857 } 858 859 @Override 860 public void setClearFutureRequests() { 861 this.setVariable(getFutureRequestsKey(principalId), getClearFutureRequestsValue()); 862 } 863 864 protected String getFutureRequestsKey(String principalId) { 865 return KewApiConstants.RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_KEY + "," + principalId + "," 866 + new Date().toString() + ", " + Math.random(); 867 } 868 869 @Override 870 public String getReceiveFutureRequestsValue() { 871 return KewApiConstants.RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_VALUE; 872 } 873 874 @Override 875 public String getDoNotReceiveFutureRequestsValue() { 876 return KewApiConstants.DONT_RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_VALUE; 877 } 878 879 @Override 880 public String getClearFutureRequestsValue() { 881 return KewApiConstants.CLEAR_FUTURE_REQUESTS_BRANCH_STATE_VALUE; 882 } 883 884 protected DocumentActionParameters constructDocumentActionParameters(String annotation) { 885 DocumentActionParameters.Builder builder = DocumentActionParameters.Builder.create(getDocumentId(), 886 getPrincipalId()); 887 builder.setAnnotation(annotation); 888 builder.setDocumentUpdate(getDocumentUpdateIfDirty()); 889 builder.setDocumentContentUpdate(getDocumentContentUpdateIfDirty()); 890 return builder.build(); 891 } 892 893 @Override 894 public DateTime getDateLastModified() { 895 return getDocument().getDateLastModified(); 896 } 897 898 @Override 899 public DateTime getDateApproved() { 900 return getDocument().getDateApproved(); 901 } 902 903 @Override 904 public DateTime getDateFinalized() { 905 return getDocument().getDateFinalized(); 906 } 907 908 @Override 909 public String getInitiatorPrincipalId() { 910 return getDocument().getInitiatorPrincipalId(); 911 } 912 913 @Override 914 public String getRoutedByPrincipalId() { 915 return getDocument().getRoutedByPrincipalId(); 916 } 917 918 @Override 919 public String getDocumentTypeId() { 920 return getDocument().getDocumentTypeId(); 921 } 922 923 @Override 924 public String getDocumentHandlerUrl() { 925 return getDocument().getDocumentHandlerUrl(); 926 } 927 928 @Override 929 public String getApplicationDocumentStatus() { 930 return getDocument().getApplicationDocumentStatus(); 931 } 932 933 @Override 934 public DateTime getApplicationDocumentStatusDate() { 935 return getDocument().getApplicationDocumentStatusDate(); 936 } 937 938 @Override 939 public Map<String, String> getVariables() { 940 return getDocument().getVariables(); 941 } 942 943 /** 944 * A wrapper around DocumentContent which keeps track of local changes and generates 945 * a new updated DocumentContent as necessary. 946 */ 947 protected static class ModifiableDocumentContent implements Serializable { 948 949 private static final long serialVersionUID = -4458431160327214042L; 950 951 private boolean dirty; 952 private DocumentContent originalDocumentContent; 953 private DocumentContentUpdate.Builder builder; 954 955 protected ModifiableDocumentContent(DocumentContent documentContent) { 956 this.dirty = false; 957 this.originalDocumentContent = documentContent; 958 this.builder = DocumentContentUpdate.Builder.create(documentContent); 959 } 960 961 protected DocumentContent getDocumentContent() { 962 if (!dirty) { 963 return originalDocumentContent; 964 } 965 DocumentContent.Builder documentContentBuilder = DocumentContent.Builder.create(originalDocumentContent); 966 documentContentBuilder.setApplicationContent(builder.getApplicationContent()); 967 documentContentBuilder.setAttributeContent(builder.getAttributeContent()); 968 documentContentBuilder.setSearchableContent(builder.getSearchableContent()); 969 return documentContentBuilder.build(); 970 } 971 972 protected DocumentContentUpdate build() { 973 return builder.build(); 974 } 975 976 protected void setDocumentContentUpdate(DocumentContentUpdate update) { 977 this.builder = DocumentContentUpdate.Builder.create(update); 978 this.dirty = true; 979 } 980 981 protected void addAttributeDefinition(WorkflowAttributeDefinition definition) { 982 builder.getAttributeDefinitions().add(definition); 983 dirty = true; 984 } 985 986 protected void removeAttributeDefinition(WorkflowAttributeDefinition definition) { 987 builder.getAttributeDefinitions().remove(definition); 988 dirty = true; 989 } 990 991 protected List<WorkflowAttributeDefinition> getAttributeDefinitions() { 992 return builder.getAttributeDefinitions(); 993 } 994 995 protected void addSearchableDefinition(WorkflowAttributeDefinition definition) { 996 builder.getSearchableDefinitions().add(definition); 997 dirty = true; 998 } 999 1000 protected void removeSearchableDefinition(WorkflowAttributeDefinition definition) { 1001 builder.getSearchableDefinitions().remove(definition); 1002 dirty = true; 1003 } 1004 1005 protected List<WorkflowAttributeDefinition> getSearchableDefinitions() { 1006 return builder.getAttributeDefinitions(); 1007 } 1008 1009 protected void setApplicationContent(String applicationContent) { 1010 builder.setApplicationContent(applicationContent); 1011 dirty = true; 1012 } 1013 1014 protected void setAttributeContent(String attributeContent) { 1015 builder.setAttributeContent(attributeContent); 1016 dirty = true; 1017 } 1018 1019 public void setAttributeDefinitions(List<WorkflowAttributeDefinition> attributeDefinitions) { 1020 builder.setAttributeDefinitions(attributeDefinitions); 1021 dirty = true; 1022 } 1023 1024 public void setSearchableContent(String searchableContent) { 1025 builder.setSearchableContent(searchableContent); 1026 dirty = true; 1027 } 1028 1029 public void setSearchableDefinitions(List<WorkflowAttributeDefinition> searchableDefinitions) { 1030 builder.setSearchableDefinitions(searchableDefinitions); 1031 dirty = true; 1032 } 1033 1034 boolean isDirty() { 1035 return dirty; 1036 } 1037 1038 } 1039 1040 /** 1041 * A wrapper around Document which keeps track of local changes and generates 1042 * a new updated Document as necessary. 1043 */ 1044 protected static class ModifiableDocument implements Serializable { 1045 1046 private static final long serialVersionUID = -3234793238863410378L; 1047 1048 private boolean dirty; 1049 private Document originalDocument; 1050 private DocumentUpdate.Builder builder; 1051 1052 protected ModifiableDocument(Document document) { 1053 this.dirty = false; 1054 this.originalDocument = document; 1055 this.builder = DocumentUpdate.Builder.create(document); 1056 } 1057 1058 protected Document getDocument() { 1059 if (!dirty) { 1060 return originalDocument; 1061 } 1062 Document.Builder documentBuilder = Document.Builder.create(originalDocument); 1063 documentBuilder.setApplicationDocumentId(builder.getApplicationDocumentId()); 1064 documentBuilder.setTitle(builder.getTitle()); 1065 documentBuilder.setApplicationDocumentStatus(builder.getApplicationDocumentStatus()); 1066 documentBuilder.setVariables(builder.getVariables()); 1067 return documentBuilder.build(); 1068 } 1069 1070 protected DocumentUpdate build() { 1071 return builder.build(); 1072 } 1073 1074 /** 1075 * Immutable value which is accessed frequently, provide direct access to it. 1076 */ 1077 protected String getDocumentId() { 1078 return originalDocument.getDocumentId(); 1079 } 1080 1081 /** 1082 * Immutable value which is accessed frequently, provide direct access to it. 1083 */ 1084 protected DateTime getDateCreated() { 1085 return originalDocument.getDateCreated(); 1086 } 1087 1088 protected String getApplicationDocumentId() { 1089 return builder.getApplicationDocumentId(); 1090 } 1091 1092 protected void setApplicationDocumentId(String applicationDocumentId) { 1093 builder.setApplicationDocumentId(applicationDocumentId); 1094 dirty = true; 1095 } 1096 1097 protected String getTitle() { 1098 return builder.getTitle(); 1099 } 1100 1101 protected void setTitle(String title) { 1102 builder.setTitle(title); 1103 dirty = true; 1104 } 1105 1106 protected String getApplicationDocumentStatus() { 1107 return builder.getApplicationDocumentStatus(); 1108 } 1109 1110 protected void setApplicationDocumentStatus(String applicationDocumentStatus) { 1111 builder.setApplicationDocumentStatus(applicationDocumentStatus); 1112 dirty = true; 1113 } 1114 1115 protected void setVariable(String name, String value) { 1116 builder.setVariable(name, value); 1117 dirty = true; 1118 } 1119 1120 protected String getVariableValue(String name) { 1121 return builder.getVariableValue(name); 1122 } 1123 1124 boolean isDirty() { 1125 return dirty; 1126 } 1127 1128 } 1129 1130 }