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 }