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 recall(String annotation, boolean cancel) { 359 DocumentActionResult result = getWorkflowDocumentActionsService().recall( 360 constructDocumentActionParameters(annotation), cancel); 361 resetStateAfterAction(result); 362 } 363 364 @Override 365 public void blanketApprove(String annotation) { 366 DocumentActionResult result = getWorkflowDocumentActionsService().blanketApprove( 367 constructDocumentActionParameters(annotation)); 368 resetStateAfterAction(result); 369 } 370 371 @Override 372 public void blanketApprove(String annotation, String... nodeNames) { 373 if (nodeNames == null) { 374 throw new IllegalArgumentException("nodeNames was null"); 375 } 376 Set<String> nodeNamesSet = new HashSet<String>(Arrays.asList(nodeNames)); 377 DocumentActionResult result = getWorkflowDocumentActionsService().blanketApproveToNodes( 378 constructDocumentActionParameters(annotation), nodeNamesSet); 379 resetStateAfterAction(result); 380 } 381 382 @Override 383 public void saveDocumentData() { 384 DocumentActionResult result = getWorkflowDocumentActionsService().saveDocumentData( 385 constructDocumentActionParameters(null)); 386 resetStateAfterAction(result); 387 } 388 389 @Override 390 public void setApplicationDocumentStatus(String applicationDocumentStatus) { 391 getModifiableDocument().setApplicationDocumentStatus(applicationDocumentStatus); 392 } 393 394 @Override 395 public void acknowledge(String annotation) { 396 DocumentActionResult result = getWorkflowDocumentActionsService().acknowledge( 397 constructDocumentActionParameters(annotation)); 398 resetStateAfterAction(result); 399 } 400 401 @Override 402 public void fyi(String annotation) { 403 DocumentActionResult result = getWorkflowDocumentActionsService().clearFyi( 404 constructDocumentActionParameters(annotation)); 405 resetStateAfterAction(result); 406 } 407 408 @Override 409 public void fyi() { 410 fyi(""); 411 } 412 413 @Override 414 public void delete() { 415 getWorkflowDocumentActionsService().delete(getDocumentId(), principalId); 416 documentDeleted = true; 417 } 418 419 @Override 420 public void refresh() { 421 Document document = getWorkflowDocumentService().getDocument(getDocumentId()); 422 this.modifiableDocument = new ModifiableDocument(document); 423 this.validActions = null; 424 this.requestedActions = null; 425 this.modifiableDocumentContent = null; 426 } 427 428 @Override 429 public void adHocToPrincipal(ActionRequestType actionRequested, String annotation, String targetPrincipalId, 430 String responsibilityDescription, boolean forceAction) { 431 adHocToPrincipal(actionRequested, null, annotation, targetPrincipalId, responsibilityDescription, forceAction); 432 } 433 434 @Override 435 public void adHocToPrincipal(ActionRequestType actionRequested, String nodeName, String annotation, 436 String targetPrincipalId, String responsibilityDescription, boolean forceAction) { 437 adHocToPrincipal(actionRequested, nodeName, annotation, targetPrincipalId, responsibilityDescription, 438 forceAction, null); 439 } 440 441 @Override 442 public void adHocToPrincipal(ActionRequestType actionRequested, String nodeName, String annotation, 443 String targetPrincipalId, String responsibilityDescription, boolean forceAction, String requestLabel) { 444 AdHocToPrincipal.Builder builder = AdHocToPrincipal.Builder 445 .create(actionRequested, nodeName, targetPrincipalId); 446 builder.setResponsibilityDescription(responsibilityDescription); 447 builder.setForceAction(forceAction); 448 builder.setRequestLabel(requestLabel); 449 DocumentActionResult result = getWorkflowDocumentActionsService().adHocToPrincipal( 450 constructDocumentActionParameters(annotation), builder.build()); 451 resetStateAfterAction(result); 452 } 453 454 @Override 455 public void adHocToPrincipal(AdHocToPrincipal adHocToPrincipal, String annotation) { 456 DocumentActionResult result = getWorkflowDocumentActionsService().adHocToPrincipal( 457 constructDocumentActionParameters(annotation), adHocToPrincipal); 458 resetStateAfterAction(result); 459 } 460 461 @Override 462 public void adHocToGroup(ActionRequestType actionRequested, String annotation, String targetGroupId, 463 String responsibilityDescription, boolean forceAction) { 464 adHocToGroup(actionRequested, null, annotation, targetGroupId, responsibilityDescription, forceAction); 465 } 466 467 @Override 468 public void adHocToGroup(ActionRequestType actionRequested, String nodeName, String annotation, 469 String targetGroupId, String responsibilityDescription, boolean forceAction) { 470 adHocToGroup(actionRequested, nodeName, annotation, targetGroupId, responsibilityDescription, forceAction, null); 471 } 472 473 @Override 474 public void adHocToGroup(ActionRequestType actionRequested, String nodeName, String annotation, 475 String targetGroupId, String responsibilityDescription, boolean forceAction, String requestLabel) { 476 AdHocToGroup.Builder builder = AdHocToGroup.Builder.create(actionRequested, nodeName, targetGroupId); 477 builder.setResponsibilityDescription(responsibilityDescription); 478 builder.setForceAction(forceAction); 479 builder.setRequestLabel(requestLabel); 480 DocumentActionResult result = getWorkflowDocumentActionsService().adHocToGroup( 481 constructDocumentActionParameters(annotation), builder.build()); 482 resetStateAfterAction(result); 483 } 484 485 @Override 486 public void adHocToGroup(AdHocToGroup adHocToGroup, String annotation) { 487 DocumentActionResult result = getWorkflowDocumentActionsService().adHocToGroup( 488 constructDocumentActionParameters(annotation), adHocToGroup); 489 resetStateAfterAction(result); 490 } 491 492 @Override 493 public void revokeAdHocRequestById(String actionRequestId, String annotation) { 494 if (StringUtils.isBlank(actionRequestId)) { 495 throw new IllegalArgumentException("actionRequestId was null or blank"); 496 } 497 DocumentActionResult result = getWorkflowDocumentActionsService().revokeAdHocRequestById( 498 constructDocumentActionParameters(annotation), actionRequestId); 499 resetStateAfterAction(result); 500 } 501 502 @Override 503 public void revokeAdHocRequests(AdHocRevoke revoke, String annotation) { 504 if (revoke == null) { 505 throw new IllegalArgumentException("revokeFromPrincipal was null"); 506 } 507 DocumentActionResult result = getWorkflowDocumentActionsService().revokeAdHocRequests( 508 constructDocumentActionParameters(annotation), revoke); 509 resetStateAfterAction(result); 510 } 511 512 @Override 513 public void revokeAllAdHocRequests(String annotation) { 514 DocumentActionResult result = getWorkflowDocumentActionsService().revokeAllAdHocRequests( 515 constructDocumentActionParameters(annotation)); 516 resetStateAfterAction(result); 517 } 518 519 @Override 520 public void setTitle(String title) { 521 getModifiableDocument().setTitle(title); 522 } 523 524 @Override 525 public String getDocumentTypeName() { 526 return getDocument().getDocumentTypeName(); 527 } 528 529 @Override 530 public boolean isCompletionRequested() { 531 return getRequestedActions().isCompleteRequested(); 532 } 533 534 @Override 535 public boolean isApprovalRequested() { 536 return getRequestedActions().isApproveRequested(); 537 } 538 539 @Override 540 public boolean isAcknowledgeRequested() { 541 return getRequestedActions().isAcknowledgeRequested(); 542 } 543 544 @Override 545 public boolean isFYIRequested() { 546 return getRequestedActions().isFyiRequested(); 547 } 548 549 @Override 550 public boolean isBlanketApproveCapable() { 551 return isValidAction(ActionType.BLANKET_APPROVE) 552 && (isCompletionRequested() || isApprovalRequested() || isInitiated()); 553 } 554 555 @Override 556 public boolean isRouteCapable() { 557 return isValidAction(ActionType.ROUTE); 558 } 559 560 @Override 561 public boolean isValidAction(ActionType actionType) { 562 if (actionType == null) { 563 throw new IllegalArgumentException("actionType was null"); 564 } 565 return getValidActions().getValidActions().contains(actionType); 566 } 567 568 @Override 569 public void superUserBlanketApprove(String annotation) { 570 DocumentActionResult result = getWorkflowDocumentActionsService().superUserBlanketApprove( 571 constructDocumentActionParameters(annotation), true); 572 resetStateAfterAction(result); 573 } 574 575 @Override 576 public void superUserNodeApprove(String nodeName, String annotation) { 577 DocumentActionResult result = getWorkflowDocumentActionsService().superUserNodeApprove( 578 constructDocumentActionParameters(annotation), true, nodeName); 579 resetStateAfterAction(result); 580 } 581 582 @Override 583 public void superUserTakeRequestedAction(String actionRequestId, String annotation) { 584 DocumentActionResult result = getWorkflowDocumentActionsService().superUserTakeRequestedAction( 585 constructDocumentActionParameters(annotation), true, actionRequestId); 586 resetStateAfterAction(result); 587 } 588 589 @Override 590 public void superUserDisapprove(String annotation) { 591 DocumentActionResult result = getWorkflowDocumentActionsService().superUserDisapprove( 592 constructDocumentActionParameters(annotation), true); 593 resetStateAfterAction(result); 594 } 595 596 @Override 597 public void superUserCancel(String annotation) { 598 DocumentActionResult result = getWorkflowDocumentActionsService().superUserCancel( 599 constructDocumentActionParameters(annotation), true); 600 resetStateAfterAction(result); 601 } 602 603 @Override 604 public void superUserReturnToPreviousNode(ReturnPoint returnPoint, String annotation) { 605 DocumentActionResult result = getWorkflowDocumentActionsService().superUserReturnToPreviousNode( 606 constructDocumentActionParameters(annotation), true, returnPoint); 607 resetStateAfterAction(result); 608 } 609 610 @Override 611 public void complete(String annotation) { 612 DocumentActionResult result = getWorkflowDocumentActionsService().complete( 613 constructDocumentActionParameters(annotation)); 614 resetStateAfterAction(result); 615 } 616 617 @Override 618 public void logAnnotation(String annotation) { 619 getWorkflowDocumentActionsService().logAnnotation(getDocumentId(), principalId, annotation); 620 } 621 622 @Override 623 public DocumentStatus getStatus() { 624 return getDocument().getStatus(); 625 } 626 627 @Override 628 public boolean checkStatus(DocumentStatus status) { 629 if (status == null) { 630 throw new IllegalArgumentException("status was null"); 631 } 632 return status == getStatus(); 633 } 634 635 /** 636 * Indicates if the document is in the initiated state or not. 637 * 638 * @return true if in the specified state 639 */ 640 @Override 641 public boolean isInitiated() { 642 return checkStatus(DocumentStatus.INITIATED); 643 } 644 645 /** 646 * Indicates if the document is in the saved state or not. 647 * 648 * @return true if in the specified state 649 */ 650 @Override 651 public boolean isSaved() { 652 return checkStatus(DocumentStatus.SAVED); 653 } 654 655 /** 656 * Indicates if the document is in the enroute state or not. 657 * 658 * @return true if in the specified state 659 */ 660 @Override 661 public boolean isEnroute() { 662 return checkStatus(DocumentStatus.ENROUTE); 663 } 664 665 /** 666 * Indicates if the document is in the exception state or not. 667 * 668 * @return true if in the specified state 669 */ 670 @Override 671 public boolean isException() { 672 return checkStatus(DocumentStatus.EXCEPTION); 673 } 674 675 /** 676 * Indicates if the document is in the canceled state or not. 677 * 678 * @return true if in the specified state 679 */ 680 @Override 681 public boolean isCanceled() { 682 return checkStatus(DocumentStatus.CANCELED); 683 } 684 685 /** 686 * Indicates if the document is in the recalled state or not. 687 * 688 * @return true if in the specified state 689 */ 690 @Override 691 public boolean isRecalled() { 692 return checkStatus(DocumentStatus.RECALLED); 693 } 694 695 /** 696 * Indicates if the document is in the disapproved state or not. 697 * 698 * @return true if in the specified state 699 */ 700 @Override 701 public boolean isDisapproved() { 702 return checkStatus(DocumentStatus.DISAPPROVED); 703 } 704 705 /** 706 * Indicates if the document is in the Processed or Finalized state. 707 * 708 * @return true if in the specified state 709 */ 710 @Override 711 public boolean isApproved() { 712 return isProcessed() || isFinal(); 713 } 714 715 /** 716 * Indicates if the document is in the processed state or not. 717 * 718 * @return true if in the specified state 719 */ 720 @Override 721 public boolean isProcessed() { 722 return checkStatus(DocumentStatus.PROCESSED); 723 } 724 725 /** 726 * Indicates if the document is in the final state or not. 727 * 728 * @return true if in the specified state 729 */ 730 @Override 731 public boolean isFinal() { 732 return checkStatus(DocumentStatus.FINAL); 733 } 734 735 /** 736 * Returns the principalId with which this WorkflowDocument was constructed 737 * 738 * @return the principalId with which this WorkflowDocument was constructed 739 */ 740 @Override 741 public String getPrincipalId() { 742 return principalId; 743 } 744 745 @Override 746 public void switchPrincipal(String principalId) { 747 if (StringUtils.isBlank(this.principalId)) { 748 throw new IllegalArgumentException("principalId was null or blank"); 749 } 750 this.principalId = principalId; 751 this.validActions = null; 752 this.requestedActions = null; 753 } 754 755 @Override 756 public void takeGroupAuthority(String annotation, String groupId) { 757 DocumentActionResult result = getWorkflowDocumentActionsService().takeGroupAuthority( 758 constructDocumentActionParameters(annotation), groupId); 759 resetStateAfterAction(result); 760 } 761 762 @Override 763 public void releaseGroupAuthority(String annotation, String groupId) { 764 DocumentActionResult result = getWorkflowDocumentActionsService().releaseGroupAuthority( 765 constructDocumentActionParameters(annotation), groupId); 766 resetStateAfterAction(result); 767 } 768 769 @Override 770 public Set<String> getNodeNames() { 771 List<RouteNodeInstance> activeNodeInstances = getActiveRouteNodeInstances(); 772 Set<String> nodeNames = new HashSet<String>(activeNodeInstances.size()); 773 for (RouteNodeInstance routeNodeInstance : activeNodeInstances) { 774 nodeNames.add(routeNodeInstance.getName()); 775 } 776 return Collections.unmodifiableSet(nodeNames); 777 } 778 779 public Set<String> getCurrentNodeNames() { 780 List<RouteNodeInstance> currentNodeInstances = getCurrentRouteNodeInstances(); 781 Set<String> nodeNames = new HashSet<String>(currentNodeInstances.size()); 782 for (RouteNodeInstance routeNodeInstance : currentNodeInstances) { 783 nodeNames.add(routeNodeInstance.getName()); 784 } 785 return Collections.unmodifiableSet(nodeNames); 786 } 787 788 @Override 789 public void returnToPreviousNode(String annotation, String nodeName) { 790 if (nodeName == null) { 791 throw new IllegalArgumentException("nodeName was null"); 792 } 793 returnToPreviousNode(annotation, ReturnPoint.create(nodeName)); 794 } 795 796 @Override 797 public void returnToPreviousNode(String annotation, ReturnPoint returnPoint) { 798 if (returnPoint == null) { 799 throw new IllegalArgumentException("returnPoint was null"); 800 } 801 DocumentActionResult result = getWorkflowDocumentActionsService().returnToPreviousNode( 802 constructDocumentActionParameters(annotation), returnPoint); 803 resetStateAfterAction(result); 804 } 805 806 @Override 807 public void move(MovePoint movePoint, String annotation) { 808 if (movePoint == null) { 809 throw new IllegalArgumentException("movePoint was null"); 810 } 811 DocumentActionResult result = getWorkflowDocumentActionsService().move( 812 constructDocumentActionParameters(annotation), movePoint); 813 resetStateAfterAction(result); 814 } 815 816 @Override 817 public List<RouteNodeInstance> getActiveRouteNodeInstances() { 818 return getWorkflowDocumentService().getActiveRouteNodeInstances(getDocumentId()); 819 } 820 821 @Override 822 public List<RouteNodeInstance> getCurrentRouteNodeInstances() { 823 return getWorkflowDocumentService().getCurrentRouteNodeInstances(getDocumentId()); 824 } 825 826 @Override 827 public List<RouteNodeInstance> getRouteNodeInstances() { 828 return getWorkflowDocumentService().getRouteNodeInstances(getDocumentId()); 829 } 830 831 @Override 832 public List<String> getPreviousNodeNames() { 833 return getWorkflowDocumentService().getPreviousRouteNodeNames(getDocumentId()); 834 } 835 836 @Override 837 public DocumentDetail getDocumentDetail() { 838 return getWorkflowDocumentService().getDocumentDetail(getDocumentId()); 839 } 840 841 @Override 842 public void updateDocumentContent(DocumentContentUpdate documentContentUpdate) { 843 if (documentContentUpdate == null) { 844 throw new IllegalArgumentException("documentContentUpdate was null."); 845 } 846 getModifiableDocumentContent().setDocumentContentUpdate(documentContentUpdate); 847 } 848 849 @Override 850 public void placeInExceptionRouting(String annotation) { 851 DocumentActionResult result = getWorkflowDocumentActionsService().placeInExceptionRouting( 852 constructDocumentActionParameters(annotation)); 853 resetStateAfterAction(result); 854 } 855 856 @Override 857 public void setVariable(String name, String value) { 858 getModifiableDocument().setVariable(name, value); 859 } 860 861 @Override 862 public String getVariableValue(String name) { 863 return getModifiableDocument().getVariableValue(name); 864 } 865 866 @Override 867 public void setReceiveFutureRequests() { 868 setVariable(getFutureRequestsKey(principalId), getReceiveFutureRequestsValue()); 869 } 870 871 @Override 872 public void setDoNotReceiveFutureRequests() { 873 this.setVariable(getFutureRequestsKey(principalId), getDoNotReceiveFutureRequestsValue()); 874 } 875 876 @Override 877 public void setClearFutureRequests() { 878 this.setVariable(getFutureRequestsKey(principalId), getClearFutureRequestsValue()); 879 } 880 881 protected String getFutureRequestsKey(String principalId) { 882 return KewApiConstants.RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_KEY + "," + principalId + "," 883 + new Date().toString() + ", " + Math.random(); 884 } 885 886 @Override 887 public String getReceiveFutureRequestsValue() { 888 return KewApiConstants.RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_VALUE; 889 } 890 891 @Override 892 public String getDoNotReceiveFutureRequestsValue() { 893 return KewApiConstants.DONT_RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_VALUE; 894 } 895 896 @Override 897 public String getClearFutureRequestsValue() { 898 return KewApiConstants.CLEAR_FUTURE_REQUESTS_BRANCH_STATE_VALUE; 899 } 900 901 protected DocumentActionParameters constructDocumentActionParameters(String annotation) { 902 DocumentActionParameters.Builder builder = DocumentActionParameters.Builder.create(getDocumentId(), 903 getPrincipalId()); 904 builder.setAnnotation(annotation); 905 builder.setDocumentUpdate(getDocumentUpdateIfDirty()); 906 builder.setDocumentContentUpdate(getDocumentContentUpdateIfDirty()); 907 return builder.build(); 908 } 909 910 @Override 911 public DateTime getDateLastModified() { 912 return getDocument().getDateLastModified(); 913 } 914 915 @Override 916 public DateTime getDateApproved() { 917 return getDocument().getDateApproved(); 918 } 919 920 @Override 921 public DateTime getDateFinalized() { 922 return getDocument().getDateFinalized(); 923 } 924 925 @Override 926 public String getInitiatorPrincipalId() { 927 return getDocument().getInitiatorPrincipalId(); 928 } 929 930 @Override 931 public String getRoutedByPrincipalId() { 932 return getDocument().getRoutedByPrincipalId(); 933 } 934 935 @Override 936 public String getDocumentTypeId() { 937 return getDocument().getDocumentTypeId(); 938 } 939 940 @Override 941 public String getDocumentHandlerUrl() { 942 return getDocument().getDocumentHandlerUrl(); 943 } 944 945 @Override 946 public String getApplicationDocumentStatus() { 947 return getDocument().getApplicationDocumentStatus(); 948 } 949 950 @Override 951 public DateTime getApplicationDocumentStatusDate() { 952 return getDocument().getApplicationDocumentStatusDate(); 953 } 954 955 @Override 956 public Map<String, String> getVariables() { 957 return getDocument().getVariables(); 958 } 959 960 /** 961 * A wrapper around DocumentContent which keeps track of local changes and generates 962 * a new updated DocumentContent as necessary. 963 */ 964 protected static class ModifiableDocumentContent implements Serializable { 965 966 private static final long serialVersionUID = -4458431160327214042L; 967 968 private boolean dirty; 969 private DocumentContent originalDocumentContent; 970 private DocumentContentUpdate.Builder builder; 971 972 protected ModifiableDocumentContent(DocumentContent documentContent) { 973 this.dirty = false; 974 this.originalDocumentContent = documentContent; 975 this.builder = DocumentContentUpdate.Builder.create(documentContent); 976 } 977 978 protected DocumentContent getDocumentContent() { 979 if (!dirty) { 980 return originalDocumentContent; 981 } 982 DocumentContent.Builder documentContentBuilder = DocumentContent.Builder.create(originalDocumentContent); 983 documentContentBuilder.setApplicationContent(builder.getApplicationContent()); 984 documentContentBuilder.setAttributeContent(builder.getAttributeContent()); 985 documentContentBuilder.setSearchableContent(builder.getSearchableContent()); 986 return documentContentBuilder.build(); 987 } 988 989 protected DocumentContentUpdate build() { 990 return builder.build(); 991 } 992 993 protected void setDocumentContentUpdate(DocumentContentUpdate update) { 994 this.builder = DocumentContentUpdate.Builder.create(update); 995 this.dirty = true; 996 } 997 998 protected void addAttributeDefinition(WorkflowAttributeDefinition definition) { 999 builder.getAttributeDefinitions().add(definition); 1000 dirty = true; 1001 } 1002 1003 protected void removeAttributeDefinition(WorkflowAttributeDefinition definition) { 1004 builder.getAttributeDefinitions().remove(definition); 1005 dirty = true; 1006 } 1007 1008 protected List<WorkflowAttributeDefinition> getAttributeDefinitions() { 1009 return builder.getAttributeDefinitions(); 1010 } 1011 1012 protected void addSearchableDefinition(WorkflowAttributeDefinition definition) { 1013 builder.getSearchableDefinitions().add(definition); 1014 dirty = true; 1015 } 1016 1017 protected void removeSearchableDefinition(WorkflowAttributeDefinition definition) { 1018 builder.getSearchableDefinitions().remove(definition); 1019 dirty = true; 1020 } 1021 1022 protected List<WorkflowAttributeDefinition> getSearchableDefinitions() { 1023 return builder.getAttributeDefinitions(); 1024 } 1025 1026 protected void setApplicationContent(String applicationContent) { 1027 builder.setApplicationContent(applicationContent); 1028 dirty = true; 1029 } 1030 1031 protected void setAttributeContent(String attributeContent) { 1032 builder.setAttributeContent(attributeContent); 1033 dirty = true; 1034 } 1035 1036 public void setAttributeDefinitions(List<WorkflowAttributeDefinition> attributeDefinitions) { 1037 builder.setAttributeDefinitions(attributeDefinitions); 1038 dirty = true; 1039 } 1040 1041 public void setSearchableContent(String searchableContent) { 1042 builder.setSearchableContent(searchableContent); 1043 dirty = true; 1044 } 1045 1046 public void setSearchableDefinitions(List<WorkflowAttributeDefinition> searchableDefinitions) { 1047 builder.setSearchableDefinitions(searchableDefinitions); 1048 dirty = true; 1049 } 1050 1051 boolean isDirty() { 1052 return dirty; 1053 } 1054 1055 } 1056 1057 /** 1058 * A wrapper around Document which keeps track of local changes and generates 1059 * a new updated Document as necessary. 1060 */ 1061 protected static class ModifiableDocument implements Serializable { 1062 1063 private static final long serialVersionUID = -3234793238863410378L; 1064 1065 private boolean dirty; 1066 private Document originalDocument; 1067 private DocumentUpdate.Builder builder; 1068 1069 protected ModifiableDocument(Document document) { 1070 this.dirty = false; 1071 this.originalDocument = document; 1072 this.builder = DocumentUpdate.Builder.create(document); 1073 } 1074 1075 protected Document getDocument() { 1076 if (!dirty) { 1077 return originalDocument; 1078 } 1079 Document.Builder documentBuilder = Document.Builder.create(originalDocument); 1080 documentBuilder.setApplicationDocumentId(builder.getApplicationDocumentId()); 1081 documentBuilder.setTitle(builder.getTitle()); 1082 return documentBuilder.build(); 1083 } 1084 1085 protected DocumentUpdate build() { 1086 return builder.build(); 1087 } 1088 1089 /** 1090 * Immutable value which is accessed frequently, provide direct access to it. 1091 */ 1092 protected String getDocumentId() { 1093 return originalDocument.getDocumentId(); 1094 } 1095 1096 /** 1097 * Immutable value which is accessed frequently, provide direct access to it. 1098 */ 1099 protected DateTime getDateCreated() { 1100 return originalDocument.getDateCreated(); 1101 } 1102 1103 protected String getApplicationDocumentId() { 1104 return builder.getApplicationDocumentId(); 1105 } 1106 1107 protected void setApplicationDocumentId(String applicationDocumentId) { 1108 builder.setApplicationDocumentId(applicationDocumentId); 1109 dirty = true; 1110 } 1111 1112 protected String getTitle() { 1113 return builder.getTitle(); 1114 } 1115 1116 protected void setTitle(String title) { 1117 builder.setTitle(title); 1118 dirty = true; 1119 } 1120 1121 protected String getApplicationDocumentStatus() { 1122 return builder.getApplicationDocumentStatus(); 1123 } 1124 1125 protected void setApplicationDocumentStatus(String applicationDocumentStatus) { 1126 builder.setApplicationDocumentStatus(applicationDocumentStatus); 1127 dirty = true; 1128 } 1129 1130 protected void setVariable(String name, String value) { 1131 builder.setVariable(name, value); 1132 dirty = true; 1133 } 1134 1135 protected String getVariableValue(String name) { 1136 return builder.getVariableValue(name); 1137 } 1138 1139 boolean isDirty() { 1140 return dirty; 1141 } 1142 1143 } 1144 1145 }