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 }