001 /** 002 * Copyright 2005-2013 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 final List<String> names = getWorkflowDocumentService().getActiveRouteNodeNames(getDocumentId()); 772 return Collections.unmodifiableSet(new HashSet<String>(names)); 773 } 774 775 public Set<String> getCurrentNodeNames() { 776 final List<String> names = getWorkflowDocumentService().getCurrentRouteNodeNames(getDocumentId()); 777 return Collections.unmodifiableSet(new HashSet<String>(names)); 778 } 779 780 @Override 781 public void returnToPreviousNode(String annotation, String nodeName) { 782 if (nodeName == null) { 783 throw new IllegalArgumentException("nodeName was null"); 784 } 785 returnToPreviousNode(annotation, ReturnPoint.create(nodeName)); 786 } 787 788 @Override 789 public void returnToPreviousNode(String annotation, ReturnPoint returnPoint) { 790 if (returnPoint == null) { 791 throw new IllegalArgumentException("returnPoint was null"); 792 } 793 DocumentActionResult result = getWorkflowDocumentActionsService().returnToPreviousNode( 794 constructDocumentActionParameters(annotation), returnPoint); 795 resetStateAfterAction(result); 796 } 797 798 @Override 799 public void move(MovePoint movePoint, String annotation) { 800 if (movePoint == null) { 801 throw new IllegalArgumentException("movePoint was null"); 802 } 803 DocumentActionResult result = getWorkflowDocumentActionsService().move( 804 constructDocumentActionParameters(annotation), movePoint); 805 resetStateAfterAction(result); 806 } 807 808 @Override 809 public List<RouteNodeInstance> getActiveRouteNodeInstances() { 810 return getWorkflowDocumentService().getActiveRouteNodeInstances(getDocumentId()); 811 } 812 813 @Override 814 public List<RouteNodeInstance> getCurrentRouteNodeInstances() { 815 return getWorkflowDocumentService().getCurrentRouteNodeInstances(getDocumentId()); 816 } 817 818 @Override 819 public List<RouteNodeInstance> getRouteNodeInstances() { 820 return getWorkflowDocumentService().getRouteNodeInstances(getDocumentId()); 821 } 822 823 @Override 824 public List<String> getPreviousNodeNames() { 825 return getWorkflowDocumentService().getPreviousRouteNodeNames(getDocumentId()); 826 } 827 828 @Override 829 public DocumentDetail getDocumentDetail() { 830 return getWorkflowDocumentService().getDocumentDetail(getDocumentId()); 831 } 832 833 @Override 834 public void updateDocumentContent(DocumentContentUpdate documentContentUpdate) { 835 if (documentContentUpdate == null) { 836 throw new IllegalArgumentException("documentContentUpdate was null."); 837 } 838 getModifiableDocumentContent().setDocumentContentUpdate(documentContentUpdate); 839 } 840 841 @Override 842 public void placeInExceptionRouting(String annotation) { 843 DocumentActionResult result = getWorkflowDocumentActionsService().placeInExceptionRouting( 844 constructDocumentActionParameters(annotation)); 845 resetStateAfterAction(result); 846 } 847 848 @Override 849 public void setVariable(String name, String value) { 850 getModifiableDocument().setVariable(name, value); 851 } 852 853 @Override 854 public String getVariableValue(String name) { 855 return getModifiableDocument().getVariableValue(name); 856 } 857 858 @Override 859 public void setReceiveFutureRequests() { 860 setVariable(getFutureRequestsKey(principalId), getReceiveFutureRequestsValue()); 861 } 862 863 @Override 864 public void setDoNotReceiveFutureRequests() { 865 this.setVariable(getFutureRequestsKey(principalId), getDoNotReceiveFutureRequestsValue()); 866 } 867 868 @Override 869 public void setClearFutureRequests() { 870 this.setVariable(getFutureRequestsKey(principalId), getClearFutureRequestsValue()); 871 } 872 873 protected String getFutureRequestsKey(String principalId) { 874 return KewApiConstants.RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_KEY + "," + principalId + "," 875 + new Date().toString() + ", " + Math.random(); 876 } 877 878 @Override 879 public String getReceiveFutureRequestsValue() { 880 return KewApiConstants.RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_VALUE; 881 } 882 883 @Override 884 public String getDoNotReceiveFutureRequestsValue() { 885 return KewApiConstants.DONT_RECEIVE_FUTURE_REQUESTS_BRANCH_STATE_VALUE; 886 } 887 888 @Override 889 public String getClearFutureRequestsValue() { 890 return KewApiConstants.CLEAR_FUTURE_REQUESTS_BRANCH_STATE_VALUE; 891 } 892 893 protected DocumentActionParameters constructDocumentActionParameters(String annotation) { 894 DocumentActionParameters.Builder builder = DocumentActionParameters.Builder.create(getDocumentId(), 895 getPrincipalId()); 896 builder.setAnnotation(annotation); 897 builder.setDocumentUpdate(getDocumentUpdateIfDirty()); 898 builder.setDocumentContentUpdate(getDocumentContentUpdateIfDirty()); 899 return builder.build(); 900 } 901 902 @Override 903 public DateTime getDateLastModified() { 904 return getDocument().getDateLastModified(); 905 } 906 907 @Override 908 public DateTime getDateApproved() { 909 return getDocument().getDateApproved(); 910 } 911 912 @Override 913 public DateTime getDateFinalized() { 914 return getDocument().getDateFinalized(); 915 } 916 917 @Override 918 public String getInitiatorPrincipalId() { 919 return getDocument().getInitiatorPrincipalId(); 920 } 921 922 @Override 923 public String getRoutedByPrincipalId() { 924 return getDocument().getRoutedByPrincipalId(); 925 } 926 927 @Override 928 public String getDocumentTypeId() { 929 return getDocument().getDocumentTypeId(); 930 } 931 932 @Override 933 public String getDocumentHandlerUrl() { 934 return getDocument().getDocumentHandlerUrl(); 935 } 936 937 @Override 938 public String getApplicationDocumentStatus() { 939 return getDocument().getApplicationDocumentStatus(); 940 } 941 942 @Override 943 public DateTime getApplicationDocumentStatusDate() { 944 return getDocument().getApplicationDocumentStatusDate(); 945 } 946 947 @Override 948 public Map<String, String> getVariables() { 949 return getDocument().getVariables(); 950 } 951 952 /** 953 * A wrapper around DocumentContent which keeps track of local changes and generates 954 * a new updated DocumentContent as necessary. 955 */ 956 protected static class ModifiableDocumentContent implements Serializable { 957 958 private static final long serialVersionUID = -4458431160327214042L; 959 960 private boolean dirty; 961 private DocumentContent originalDocumentContent; 962 private DocumentContentUpdate.Builder builder; 963 964 protected ModifiableDocumentContent(DocumentContent documentContent) { 965 this.dirty = false; 966 this.originalDocumentContent = documentContent; 967 this.builder = DocumentContentUpdate.Builder.create(documentContent); 968 } 969 970 protected DocumentContent getDocumentContent() { 971 if (!dirty) { 972 return originalDocumentContent; 973 } 974 DocumentContent.Builder documentContentBuilder = DocumentContent.Builder.create(originalDocumentContent); 975 documentContentBuilder.setApplicationContent(builder.getApplicationContent()); 976 documentContentBuilder.setAttributeContent(builder.getAttributeContent()); 977 documentContentBuilder.setSearchableContent(builder.getSearchableContent()); 978 return documentContentBuilder.build(); 979 } 980 981 protected DocumentContentUpdate build() { 982 return builder.build(); 983 } 984 985 protected void setDocumentContentUpdate(DocumentContentUpdate update) { 986 this.builder = DocumentContentUpdate.Builder.create(update); 987 this.dirty = true; 988 } 989 990 protected void addAttributeDefinition(WorkflowAttributeDefinition definition) { 991 builder.getAttributeDefinitions().add(definition); 992 dirty = true; 993 } 994 995 protected void removeAttributeDefinition(WorkflowAttributeDefinition definition) { 996 builder.getAttributeDefinitions().remove(definition); 997 dirty = true; 998 } 999 1000 protected List<WorkflowAttributeDefinition> getAttributeDefinitions() { 1001 return builder.getAttributeDefinitions(); 1002 } 1003 1004 protected void addSearchableDefinition(WorkflowAttributeDefinition definition) { 1005 builder.getSearchableDefinitions().add(definition); 1006 dirty = true; 1007 } 1008 1009 protected void removeSearchableDefinition(WorkflowAttributeDefinition definition) { 1010 builder.getSearchableDefinitions().remove(definition); 1011 dirty = true; 1012 } 1013 1014 protected List<WorkflowAttributeDefinition> getSearchableDefinitions() { 1015 return builder.getAttributeDefinitions(); 1016 } 1017 1018 protected void setApplicationContent(String applicationContent) { 1019 builder.setApplicationContent(applicationContent); 1020 dirty = true; 1021 } 1022 1023 protected void setAttributeContent(String attributeContent) { 1024 builder.setAttributeContent(attributeContent); 1025 dirty = true; 1026 } 1027 1028 public void setAttributeDefinitions(List<WorkflowAttributeDefinition> attributeDefinitions) { 1029 builder.setAttributeDefinitions(attributeDefinitions); 1030 dirty = true; 1031 } 1032 1033 public void setSearchableContent(String searchableContent) { 1034 builder.setSearchableContent(searchableContent); 1035 dirty = true; 1036 } 1037 1038 public void setSearchableDefinitions(List<WorkflowAttributeDefinition> searchableDefinitions) { 1039 builder.setSearchableDefinitions(searchableDefinitions); 1040 dirty = true; 1041 } 1042 1043 boolean isDirty() { 1044 return dirty; 1045 } 1046 1047 } 1048 1049 /** 1050 * A wrapper around Document which keeps track of local changes and generates 1051 * a new updated Document as necessary. 1052 */ 1053 protected static class ModifiableDocument implements Serializable { 1054 1055 private static final long serialVersionUID = -3234793238863410378L; 1056 1057 private boolean dirty; 1058 private Document originalDocument; 1059 private DocumentUpdate.Builder builder; 1060 1061 protected ModifiableDocument(Document document) { 1062 this.dirty = false; 1063 this.originalDocument = document; 1064 this.builder = DocumentUpdate.Builder.create(document); 1065 } 1066 1067 protected Document getDocument() { 1068 if (!dirty) { 1069 return originalDocument; 1070 } 1071 Document.Builder documentBuilder = Document.Builder.create(originalDocument); 1072 documentBuilder.setApplicationDocumentId(builder.getApplicationDocumentId()); 1073 documentBuilder.setTitle(builder.getTitle()); 1074 documentBuilder.setApplicationDocumentStatus(builder.getApplicationDocumentStatus()); 1075 documentBuilder.setVariables(builder.getVariables()); 1076 return documentBuilder.build(); 1077 } 1078 1079 protected DocumentUpdate build() { 1080 return builder.build(); 1081 } 1082 1083 /** 1084 * Immutable value which is accessed frequently, provide direct access to it. 1085 */ 1086 protected String getDocumentId() { 1087 return originalDocument.getDocumentId(); 1088 } 1089 1090 /** 1091 * Immutable value which is accessed frequently, provide direct access to it. 1092 */ 1093 protected DateTime getDateCreated() { 1094 return originalDocument.getDateCreated(); 1095 } 1096 1097 protected String getApplicationDocumentId() { 1098 return builder.getApplicationDocumentId(); 1099 } 1100 1101 protected void setApplicationDocumentId(String applicationDocumentId) { 1102 builder.setApplicationDocumentId(applicationDocumentId); 1103 dirty = true; 1104 } 1105 1106 protected String getTitle() { 1107 return builder.getTitle(); 1108 } 1109 1110 protected void setTitle(String title) { 1111 builder.setTitle(title); 1112 dirty = true; 1113 } 1114 1115 protected String getApplicationDocumentStatus() { 1116 return builder.getApplicationDocumentStatus(); 1117 } 1118 1119 protected void setApplicationDocumentStatus(String applicationDocumentStatus) { 1120 builder.setApplicationDocumentStatus(applicationDocumentStatus); 1121 dirty = true; 1122 } 1123 1124 protected void setVariable(String name, String value) { 1125 builder.setVariable(name, value); 1126 dirty = true; 1127 } 1128 1129 protected String getVariableValue(String name) { 1130 return builder.getVariableValue(name); 1131 } 1132 1133 boolean isDirty() { 1134 return dirty; 1135 } 1136 1137 } 1138 1139 }