0001: /*
0002: * Copyright 2005-2006 The Kuali Foundation.
0003: *
0004: *
0005: * Licensed under the Educational Community License, Version 1.0 (the "License");
0006: * you may not use this file except in compliance with the License.
0007: * You may obtain a copy of the License at
0008: *
0009: * http://www.opensource.org/licenses/ecl1.php
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package edu.iu.uis.eden.clientapp;
0018:
0019: import java.rmi.RemoteException;
0020: import java.sql.Timestamp;
0021: import java.util.ArrayList;
0022: import java.util.Calendar;
0023: import java.util.List;
0024:
0025: import org.kuali.rice.config.RiceConfigurer;
0026: import org.kuali.rice.resourceloader.GlobalResourceLoader;
0027: import org.kuali.workflow.config.KEWConfigurer;
0028:
0029: import edu.iu.uis.eden.EdenConstants;
0030: import edu.iu.uis.eden.KEWServiceLocator;
0031: import edu.iu.uis.eden.clientapp.vo.ActionRequestVO;
0032: import edu.iu.uis.eden.clientapp.vo.ActionTakenVO;
0033: import edu.iu.uis.eden.clientapp.vo.AdHocRevokeVO;
0034: import edu.iu.uis.eden.clientapp.vo.DocumentContentVO;
0035: import edu.iu.uis.eden.clientapp.vo.DocumentDetailVO;
0036: import edu.iu.uis.eden.clientapp.vo.EmplIdVO;
0037: import edu.iu.uis.eden.clientapp.vo.ModifiableDocumentContentVO;
0038: import edu.iu.uis.eden.clientapp.vo.MovePointVO;
0039: import edu.iu.uis.eden.clientapp.vo.NetworkIdVO;
0040: import edu.iu.uis.eden.clientapp.vo.NoteVO;
0041: import edu.iu.uis.eden.clientapp.vo.ResponsiblePartyVO;
0042: import edu.iu.uis.eden.clientapp.vo.ReturnPointVO;
0043: import edu.iu.uis.eden.clientapp.vo.RouteHeaderVO;
0044: import edu.iu.uis.eden.clientapp.vo.RouteNodeInstanceVO;
0045: import edu.iu.uis.eden.clientapp.vo.RouteTemplateEntryVO;
0046: import edu.iu.uis.eden.clientapp.vo.UserIdVO;
0047: import edu.iu.uis.eden.clientapp.vo.UuIdVO;
0048: import edu.iu.uis.eden.clientapp.vo.WorkflowAttributeDefinitionVO;
0049: import edu.iu.uis.eden.clientapp.vo.WorkflowAttributeValidationErrorVO;
0050: import edu.iu.uis.eden.clientapp.vo.WorkflowIdVO;
0051: import edu.iu.uis.eden.clientapp.vo.WorkgroupIdVO;
0052: import edu.iu.uis.eden.exception.DocumentTypeNotFoundException;
0053: import edu.iu.uis.eden.exception.WorkflowException;
0054: import edu.iu.uis.eden.exception.WorkflowRuntimeException;
0055: import edu.iu.uis.eden.server.WorkflowDocumentActions;
0056: import edu.iu.uis.eden.server.WorkflowUtility;
0057: import edu.iu.uis.eden.util.Utilities;
0058:
0059: /**
0060: * Represents a document in Workflow from the perspective of the client. This class is one of two
0061: * (Java) client interfaces to the KEW system (the other being {@link WorkflowInfo} class). The
0062: * first time an instance of this class is created, it will read the client configuration to determine
0063: * how to connect to KEW.
0064: *
0065: * <p>This class is used by creating new instances using the appropriate constructor. To create a new
0066: * document in KEW, create an instance of this class passing a UserIdVO and a
0067: * document type name. To load an existing document, create an instance of this class passing a
0068: * UserIdVO and a document ID number.
0069: *
0070: * <p>Internally, this wrapper interacts with the {@link WorkflowDocumentActions} service exported
0071: * over the KSB, maintaining state.
0072: *
0073: * <p>This class is not thread safe and must by synchronized externally for concurrent access.
0074: *
0075: * @author rkirkend
0076: * @author ewestfal
0077: */
0078: public class WorkflowDocument implements java.io.Serializable {
0079:
0080: private static final long serialVersionUID = -3672966990721719088L;
0081:
0082: /**
0083: * UserId VO of the user as whom actions will be taken on the KEW document
0084: */
0085: private UserIdVO userId;
0086: /**
0087: * RouteHeader VO of the KEW document this WorkflowDocument represents
0088: */
0089: private RouteHeaderVO routeHeader;
0090: /**
0091: * Flag that indicates whether the document content currently loaded needs to be refreshed.
0092: * This is the case either if the document content has not yet been loaded, or an action
0093: * that might possibly affect the document content (which is potentially any action) has
0094: * subsequently been taken on the document through this API.
0095: */
0096: private boolean documentContentDirty = false;
0097: /**
0098: * Value Object encapsulating the document content
0099: */
0100: private ModifiableDocumentContentVO documentContent;
0101:
0102: /**
0103: * Constructs a WorkflowDocument representing a new document in the workflow system.
0104: * Creation/committing of the new document is deferred until the first action is
0105: * taken on the document.
0106: * @param userId the user as which to take actions on the document
0107: * @param documentType the type of the document to create
0108: * @throws WorkflowException if anything goes awry
0109: */
0110: public WorkflowDocument(UserIdVO userId, String documentType)
0111: throws WorkflowException {
0112: init(userId, documentType, null);
0113: }
0114:
0115: /**
0116: * Loads a workflow document with the given route header ID for the given User. If no document
0117: * can be found with the given ID, then the {@link getRouteHeader()} method of the WorkflowDocument
0118: * which is created will return null.
0119: *
0120: * @throws WorkflowException if there is a problem loading the WorkflowDocument
0121: */
0122: public WorkflowDocument(UserIdVO userId, Long routeHeaderId)
0123: throws WorkflowException {
0124: init(userId, null, routeHeaderId);
0125: }
0126:
0127: /**
0128: * Initializes this WorkflowDocument object, by either attempting to load an existing document by routeHeaderid
0129: * if one is supplied (non-null), or by constructing an empty document of the specified type.
0130: * @param userId the user under which actions via this API on the specified document will be taken
0131: * @param documentType the type of document this WorkflowDocument should represent (either this parameter or routeHeaderId must be specified, non-null)
0132: * @param routeHeaderId the id of an existing document to load (either this parameter or documentType must be specified, non-null)
0133: * @throws WorkflowException if a routeHeaderId is specified but an exception occurs trying to load the document route header
0134: */
0135: private void init(UserIdVO userId, String documentType,
0136: Long routeHeaderId) throws WorkflowException {
0137: try {
0138: this .userId = userId;
0139: routeHeader = new RouteHeaderVO();
0140: routeHeader.setDocTypeName(documentType);
0141: if (routeHeaderId != null) {
0142: routeHeader = getWorkflowUtility()
0143: .getRouteHeaderWithUser(userId, routeHeaderId);
0144: }
0145: } catch (Exception e) {
0146: throw handleException(e);
0147: }
0148: }
0149:
0150: /**
0151: * Retrieves the WorkflowUtility proxy from the locator. The locator will cache this for us.
0152: */
0153: private WorkflowUtility getWorkflowUtility()
0154: throws WorkflowException {
0155: initializeBus();
0156: return (WorkflowUtility) GlobalResourceLoader
0157: .getService(KEWServiceLocator.WORKFLOW_UTILITY_SERVICE);
0158: }
0159:
0160: /**
0161: * Retrieves the WorkflowDocumentActions proxy from the locator. The locator will cache this for us.
0162: */
0163: private WorkflowDocumentActions getWorkflowDocumentActions()
0164: throws WorkflowException {
0165: initializeBus();
0166: return (WorkflowDocumentActions) GlobalResourceLoader
0167: .getService(KEWServiceLocator.WORKFLOW_DOCUMENT_ACTIONS_SERVICE);
0168: }
0169:
0170: /**
0171: * Initializes the KSB configuration if it has not already been initialized by the application;
0172: * in that case only the KEW configurer is added.
0173: * @throws WorkflowException if there is an error starting the RiceConfigurer
0174: */
0175: private synchronized void initializeBus() throws WorkflowException {
0176: if (!GlobalResourceLoader.isInitialized()) {
0177: RiceConfigurer configurer = new RiceConfigurer();
0178: configurer
0179: .setMessageEntity(EdenConstants.KEW_MESSAGING_ENTITY);
0180: configurer.getModules().add(new KEWConfigurer());
0181: try {
0182: configurer.start();
0183: } catch (Exception e) {
0184: if (e instanceof WorkflowException) {
0185: throw (WorkflowException) e;
0186: } else if (e instanceof RuntimeException) {
0187: throw (RuntimeException) e;
0188: }
0189: throw new WorkflowException(e);
0190: }
0191: }
0192: }
0193:
0194: // ########################
0195: // Document Content methods
0196: // ########################
0197:
0198: /**
0199: * Returns an up-to-date DocumentContent of this document.
0200: * @see WorkflowUtility#getDocumentContent(Long)
0201: */
0202: public DocumentContentVO getDocumentContent() {
0203: try {
0204: // create the document if it hasn't already been created
0205: if (getRouteHeader().getRouteHeaderId() == null) {
0206: routeHeader = getWorkflowDocumentActions()
0207: .createDocument(userId, getRouteHeader());
0208: }
0209: if (documentContent == null || documentContentDirty) {
0210: documentContent = new ModifiableDocumentContentVO(
0211: getWorkflowUtility().getDocumentContent(
0212: routeHeader.getRouteHeaderId()));
0213: documentContentDirty = false;
0214: }
0215: } catch (Exception e) {
0216: throw handleExceptionAsRuntime(e);
0217: }
0218: return documentContent;
0219: }
0220:
0221: /**
0222: * Returns the application specific section of the document content. This is
0223: * a convenience method that delegates to the {@link DocumentContentVO}.
0224: *
0225: * For documents routed prior to Workflow 2.0:
0226: * If the application did NOT use attributes for XML generation, this method will
0227: * return the entire document content XML. Otherwise it will return the empty string.
0228: * @see DocumentContentVO#getApplicationContent()
0229: */
0230: public String getApplicationContent() {
0231: return getDocumentContent().getApplicationContent();
0232: }
0233:
0234: /**
0235: * Sets the application specific section of the document content. This is
0236: * a convenience method that delegates to the {@link DocumentContentVO}.
0237: */
0238: public void setApplicationContent(String applicationContent) {
0239: getDocumentContent().setApplicationContent(applicationContent);
0240: }
0241:
0242: /**
0243: * Clears all attribute document content from the document.
0244: * Typically, this will be used if it is necessary to update the attribute doc content on
0245: * the document. This can be accomplished by clearing the content and then adding the
0246: * desired attribute definitions.
0247: *
0248: * This is a convenience method that delegates to the {@link DocumentContentVO}.
0249: *
0250: * In order for these changes to take effect, an action must be performed on the document (such as "save").
0251: */
0252: public void clearAttributeContent() {
0253: getDocumentContent().setAttributeContent("");
0254: }
0255:
0256: /**
0257: * Returns the attribute-generated section of the document content. This is
0258: * a convenience method that delegates to the {@link DocumentContentVO}.
0259: * @see DocumentContentVO#getAttributeContent()
0260: */
0261: public String getAttributeContent() {
0262: return getDocumentContent().getAttributeContent();
0263: }
0264:
0265: /**
0266: * Adds an attribute definition which defines creation parameters for a WorkflowAttribute
0267: * implementation. The created attribute will be used to generate attribute document content.
0268: * When the document is sent to the server, this will be appended to the existing attribute
0269: * doc content. If it is required to replace the attribute document content, then the
0270: * clearAttributeContent() method should be invoked prior to adding attribute definitions.
0271: *
0272: * This is a convenience method that delegates to the {@link DocumentContentVO}.
0273: * @see DocumentContentVO#addAttributeDefinition(WorkflowAttributeDefinitionVO)
0274: */
0275: public void addAttributeDefinition(
0276: WorkflowAttributeDefinitionVO attributeDefinition) {
0277: getDocumentContent()
0278: .addAttributeDefinition(attributeDefinition);
0279: }
0280:
0281: /**
0282: * Validate the WorkflowAttributeDefinition against it's attribute on the server. This will validate
0283: * the inputs that will eventually become xml.
0284: *
0285: * Only applies to attributes implementing WorkflowAttributeXmlValidator.
0286: *
0287: * This is a call through to the WorkflowInfo object and is here for convenience.
0288: *
0289: * @param attributeDefinition the workflow attribute definition VO to validate
0290: * @return WorkflowAttributeValidationErrorVO[] of error from the attribute
0291: * @throws WorkflowException when attribute doesn't implement WorkflowAttributeXmlValidator
0292: * @see WorkflowUtility#validateWorkflowAttributeDefinitionVO(WorkflowAttributeDefinitionVO)
0293: */
0294: public WorkflowAttributeValidationErrorVO[] validateAttributeDefinition(
0295: WorkflowAttributeDefinitionVO attributeDefinition)
0296: throws WorkflowException {
0297: try {
0298: return getWorkflowUtility()
0299: .validateWorkflowAttributeDefinitionVO(
0300: attributeDefinition);
0301: } catch (Exception e) {
0302: throw handleException(e);
0303: }
0304: }
0305:
0306: /**
0307: * Removes an attribute definition from the document content. This is
0308: * a convenience method that delegates to the {@link DocumentContentVO}.
0309: * @param attributeDefinition the attribute definition VO to remove
0310: */
0311: public void removeAttributeDefinition(
0312: WorkflowAttributeDefinitionVO attributeDefinition) {
0313: getDocumentContent().removeAttributeDefinition(
0314: attributeDefinition);
0315: }
0316:
0317: /**
0318: * Removes all attribute definitions from the document content. This is
0319: * a convenience method that delegates to the {@link DocumentContentVO}.
0320: */
0321: public void clearAttributeDefinitions() {
0322: getDocumentContent().setAttributeDefinitions(
0323: new WorkflowAttributeDefinitionVO[0]);
0324: }
0325:
0326: /**
0327: * Returns the attribute definition VOs currently defined on the document content. This is
0328: * a convenience method that delegates to the {@link DocumentContentVO}.
0329: * @return the attribute definition VOs currently defined on the document content.
0330: * @see DocumentContentVO#getAttributeDefinitions()
0331: */
0332: public WorkflowAttributeDefinitionVO[] getAttributeDefinitions() {
0333: return getDocumentContent().getAttributeDefinitions();
0334: }
0335:
0336: /**
0337: * Adds a searchable attribute definition which defines creation parameters for a SearchableAttribute
0338: * implementation. The created attribute will be used to generate searchable document content.
0339: * When the document is sent to the server, this will be appended to the existing searchable
0340: * doc content. If it is required to replace the searchable document content, then the
0341: * clearSearchableContent() method should be invoked prior to adding definitions. This is
0342: * a convenience method that delegates to the {@link DocumentContentVO}.
0343: */
0344: public void addSearchableDefinition(
0345: WorkflowAttributeDefinitionVO searchableDefinition) {
0346: getDocumentContent().addSearchableDefinition(
0347: searchableDefinition);
0348: }
0349:
0350: /**
0351: * Removes a searchable attribute definition from the document content. This is
0352: * a convenience method that delegates to the {@link DocumentContentVO}.
0353: * @param searchableDefinition the searchable attribute definition to remove
0354: */
0355: public void removeSearchableDefinition(
0356: WorkflowAttributeDefinitionVO searchableDefinition) {
0357: getDocumentContent().removeSearchableDefinition(
0358: searchableDefinition);
0359: }
0360:
0361: /**
0362: * Removes all searchable attribute definitions from the document content. This is
0363: * a convenience method that delegates to the {@link DocumentContentVO}.
0364: */
0365: public void clearSearchableDefinitions() {
0366: getDocumentContent().setSearchableDefinitions(
0367: new WorkflowAttributeDefinitionVO[0]);
0368: }
0369:
0370: /**
0371: * Clears the searchable content from the document content. This is
0372: * a convenience method that delegates to the {@link DocumentContentVO}.
0373: */
0374: public void clearSearchableContent() {
0375: getDocumentContent().setSearchableContent("");
0376: }
0377:
0378: /**
0379: * Returns the searchable attribute definitions on the document content. This is
0380: * a convenience method that delegates to the {@link DocumentContentVO}.
0381: * @return the searchable attribute definitions on the document content.
0382: * @see DocumentContentVO#getSearchableDefinitions()
0383: */
0384: public WorkflowAttributeDefinitionVO[] getSearchableDefinitions() {
0385: return getDocumentContent().getSearchableDefinitions();
0386: }
0387:
0388: // ########################
0389: // END Document Content methods
0390: // ########################
0391:
0392: /**
0393: * Returns the RouteHeaderVO for the workflow document this WorkflowDocument represents
0394: */
0395: public RouteHeaderVO getRouteHeader() {
0396: return routeHeader;
0397: }
0398:
0399: /**
0400: * Returns the id of the workflow document this WorkflowDocument represents. If this is a new document
0401: * that has not yet been created, the document is first created (and therefore this will return a new id)
0402: * @return the id of the workflow document this WorkflowDocument represents
0403: * @throws WorkflowException if an error occurs during document creation
0404: */
0405: public Long getRouteHeaderId() throws WorkflowException {
0406: try {
0407: createDocumentIfNeccessary();
0408: return getRouteHeader().getRouteHeaderId();
0409: } catch (Exception e) {
0410: throw handleException(e);
0411: }
0412: }
0413:
0414: /**
0415: * Returns VOs of the pending ActionRequests on this document. If this object represents a new document
0416: * that has not yet been created, then an empty array will be returned. The ordering of ActionRequests
0417: * returned by this method is not guaranteed.
0418: *
0419: * This method relies on the WorkflowUtility service
0420: *
0421: * @return VOs of the pending ActionRequests on this document
0422: * @throws WorkflowException if an error occurs obtaining the pending action requests for this document
0423: * @see WorkflowUtility#getActionRequests(Long)
0424: */
0425: public ActionRequestVO[] getActionRequests()
0426: throws WorkflowException {
0427: if (getRouteHeaderId() == null) {
0428: return new ActionRequestVO[0];
0429: }
0430: try {
0431: return getWorkflowUtility().getActionRequests(
0432: getRouteHeaderId());
0433: } catch (Exception e) {
0434: throw handleException(e);
0435: }
0436: }
0437:
0438: /**
0439: * Returns VOs of the actions taken on this document. If this object represents a new document
0440: * that has not yet been created, then an empty array will be returned. The ordering of actions taken
0441: * returned by this method is not guaranteed.
0442: *
0443: * This method relies on the WorkflowUtility service
0444: *
0445: * @return VOs of the actions that have been taken on this document
0446: * @throws WorkflowException if an error occurs obtaining the actions taken on this document
0447: * @see WorkflowUtility#getActionsTaken(Long)
0448: */
0449: public ActionTakenVO[] getActionsTaken() throws WorkflowException {
0450: if (getRouteHeaderId() == null) {
0451: return new ActionTakenVO[0];
0452: }
0453: try {
0454: return getWorkflowUtility().getActionsTaken(
0455: getRouteHeaderId());
0456: } catch (RemoteException e) {
0457: throw handleException(e);
0458: }
0459: }
0460:
0461: /**
0462: * Sets the "application doc id" on the document
0463: * @param appDocId the "application doc id" to set on the workflow document
0464: */
0465: public void setAppDocId(String appDocId) {
0466: routeHeader.setAppDocId(appDocId);
0467: }
0468:
0469: /**
0470: * Returns the "application doc id" set on this workflow document (if any)
0471: * @return the "application doc id" set on this workflow document (if any)
0472: */
0473: public String getAppDocId() {
0474: return routeHeader.getAppDocId();
0475: }
0476:
0477: /**
0478: * Returns the date/time the document was created, or null if the document has not yet been created
0479: * @return the date/time the document was created, or null if the document has not yet been created
0480: */
0481: public Timestamp getDateCreated() {
0482: return Utilities.convertCalendar(routeHeader.getDateCreated());
0483: }
0484:
0485: /**
0486: * Returns the title of the document
0487: * @return the title of the document
0488: */
0489: public String getTitle() {
0490: return getRouteHeader().getDocTitle();
0491: }
0492:
0493: /**
0494: * Performs the 'save' action on the document this WorkflowDocument represents. If this is a new document,
0495: * the document is created first.
0496: * @param annotation the message to log for the action
0497: * @throws WorkflowException in case an error occurs saving the document
0498: * @see WorkflowDocumentActions#saveDocument(UserIdVO, RouteHeaderVO, String)
0499: */
0500: public void saveDocument(String annotation)
0501: throws WorkflowException {
0502: try {
0503: createDocumentIfNeccessary();
0504: routeHeader = getWorkflowDocumentActions().saveDocument(
0505: userId, getRouteHeader(), annotation);
0506: documentContentDirty = true;
0507: } catch (Exception e) {
0508: throw handleException(e);
0509: }
0510: }
0511:
0512: /**
0513: * Performs the 'route' action on the document this WorkflowDocument represents. If this is a new document,
0514: * the document is created first.
0515: * @param annotation the message to log for the action
0516: * @throws WorkflowException in case an error occurs routing the document
0517: * @see WorkflowDocumentActions#routeDocument(UserIdVO, RouteHeaderVO, String)
0518: */
0519: public void routeDocument(String annotation)
0520: throws WorkflowException {
0521: try {
0522: createDocumentIfNeccessary();
0523: routeHeader = getWorkflowDocumentActions().routeDocument(
0524: userId, routeHeader, annotation);
0525: documentContentDirty = true;
0526: } catch (Exception e) {
0527: throw handleException(e);
0528: }
0529: }
0530:
0531: /**
0532: * Performs the 'disapprove' action on the document this WorkflowDocument represents. If this is a new document,
0533: * the document is created first.
0534: * @param annotation the message to log for the action
0535: * @throws WorkflowException in case an error occurs disapproving the document
0536: * @see WorkflowDocumentActions#disapproveDocument(UserIdVO, RouteHeaderVO, String)
0537: */
0538: public void disapprove(String annotation) throws WorkflowException {
0539: try {
0540: createDocumentIfNeccessary();
0541: routeHeader = getWorkflowDocumentActions()
0542: .disapproveDocument(userId, getRouteHeader(),
0543: annotation);
0544: documentContentDirty = true;
0545: } catch (Exception e) {
0546: throw handleException(e);
0547: }
0548:
0549: }
0550:
0551: /**
0552: * Performs the 'approve' action on the document this WorkflowDocument represents. If this is a new document,
0553: * the document is created first.
0554: * @param annotation the message to log for the action
0555: * @throws WorkflowException in case an error occurs approving the document
0556: * @see WorkflowDocumentActions#approveDocument(UserIdVO, RouteHeaderVO, String)
0557: */
0558: public void approve(String annotation) throws WorkflowException {
0559: try {
0560: createDocumentIfNeccessary();
0561: routeHeader = getWorkflowDocumentActions().approveDocument(
0562: userId, getRouteHeader(), annotation);
0563: documentContentDirty = true;
0564: } catch (Exception e) {
0565: throw new RuntimeException(e);
0566: }
0567: }
0568:
0569: /**
0570: * Performs the 'cancel' action on the document this WorkflowDocument represents. If this is a new document,
0571: * the document is created first.
0572: * @param annotation the message to log for the action
0573: * @throws WorkflowException in case an error occurs canceling the document
0574: * @see WorkflowDocumentActions#cancelDocument(UserIdVO, RouteHeaderVO, String)
0575: */
0576: public void cancel(String annotation) throws WorkflowException {
0577: try {
0578: createDocumentIfNeccessary();
0579: routeHeader = getWorkflowDocumentActions().cancelDocument(
0580: userId, getRouteHeader(), annotation);
0581: documentContentDirty = true;
0582: } catch (Exception e) {
0583: throw handleException(e);
0584: }
0585: }
0586:
0587: /**
0588: * Performs the 'blanket-approve' action on the document this WorkflowDocument represents. If this is a new document,
0589: * the document is created first.
0590: * @param annotation the message to log for the action
0591: * @throws WorkflowException in case an error occurs blanket-approving the document
0592: * @see WorkflowDocumentActions#blanketApprovalToNodes(UserIdVO, RouteHeaderVO, String, String[])
0593: */
0594: public void blanketApprove(String annotation)
0595: throws WorkflowException {
0596: blanketApprove(annotation, (String) null);
0597: }
0598:
0599: /**
0600: * Commits any changes made to the local copy of this document to the workflow system. If this is a new document,
0601: * the document is created first.
0602: * @throws WorkflowException in case an error occurs saving the document
0603: * @see WorkflowDocumentActions#saveRoutingData(UserIdVO, RouteHeaderVO)
0604: */
0605: public void saveRoutingData() throws WorkflowException {
0606: try {
0607: createDocumentIfNeccessary();
0608: routeHeader = getWorkflowDocumentActions().saveRoutingData(
0609: userId, getRouteHeader());
0610: documentContentDirty = true;
0611: } catch (Exception e) {
0612: throw handleException(e);
0613: }
0614: }
0615:
0616: /**
0617: * Performs the 'acknowledge' action on the document this WorkflowDocument represents. If this is a new document,
0618: * the document is created first.
0619: * @param annotation the message to log for the action
0620: * @throws WorkflowException in case an error occurs acknowledging the document
0621: * @see WorkflowDocumentActions#acknowledgeDocument(UserIdVO, RouteHeaderVO, String)
0622: */
0623: public void acknowledge(String annotation) throws WorkflowException {
0624: try {
0625: createDocumentIfNeccessary();
0626: routeHeader = getWorkflowDocumentActions()
0627: .acknowledgeDocument(userId, getRouteHeader(),
0628: annotation);
0629: documentContentDirty = true;
0630: } catch (Exception e) {
0631: throw handleException(e);
0632: }
0633: }
0634:
0635: /**
0636: * Performs the 'fyi' action on the document this WorkflowDocument represents. If this is a new document,
0637: * the document is created first.
0638: * @param annotation the message to log for the action
0639: * @throws WorkflowException in case an error occurs fyi-ing the document
0640: */
0641: public void fyi() throws WorkflowException {
0642: try {
0643: createDocumentIfNeccessary();
0644: routeHeader = getWorkflowDocumentActions()
0645: .clearFYIDocument(userId, getRouteHeader());
0646: documentContentDirty = true;
0647: } catch (Exception e) {
0648: throw handleException(e);
0649: }
0650: }
0651:
0652: /**
0653: * Performs the 'delete' action on the document this WorkflowDocument represents. If this is a new document,
0654: * the document is created first.
0655: * @param annotation the message to log for the action
0656: * @throws WorkflowException in case an error occurs deleting the document
0657: * @see WorkflowDocumentActions#deleteDocument(UserIdVO, RouteHeaderVO)
0658: */
0659: public void delete() throws WorkflowException {
0660: try {
0661: createDocumentIfNeccessary();
0662: getWorkflowDocumentActions().deleteDocument(userId,
0663: getRouteHeader());
0664: documentContentDirty = true;
0665: } catch (Exception e) {
0666: throw handleException(e);
0667: }
0668: }
0669:
0670: /**
0671: * Reloads the document route header. If this is a new document, the document is created first.
0672: * Next time document content is accessed, an up-to-date copy will be retrieved from workflow.
0673: * @throws WorkflowException in case an error occurs retrieving the route header
0674: */
0675: public void refreshContent() throws WorkflowException {
0676: try {
0677: createDocumentIfNeccessary();
0678: routeHeader = getWorkflowUtility().getRouteHeader(
0679: getRouteHeaderId());
0680: documentContentDirty = true;
0681: } catch (Exception e) {
0682: throw handleException(e);
0683: }
0684: }
0685:
0686: /**
0687: * @deprecated use {@link #appSpecificRouteDocumentToUser(String, String, String, UserIdVO, String, boolean)}
0688: */
0689: public void appSpecificRouteDocumentToUser(String actionRequested,
0690: String nodeName, int priority, String annotation,
0691: UserIdVO recipient, String responsibilityDesc,
0692: boolean ignorePreviousActions) throws WorkflowException {
0693: appSpecificRouteDocumentToUser(actionRequested, nodeName,
0694: annotation, recipient, responsibilityDesc,
0695: ignorePreviousActions);
0696: }
0697:
0698: /**
0699: * @deprecated use {@link #appSpecificRouteDocumentToWorkgroup(String, String, String, WorkgroupIdVO, String, boolean)}
0700: */
0701: public void appSpecificRouteDocumentToWorkgroup(
0702: String actionRequested, String nodeName, int priority,
0703: String annotation, WorkgroupIdVO workgroupId,
0704: String responsibilityDesc, boolean ignorePreviousActions)
0705: throws WorkflowException {
0706: appSpecificRouteDocumentToWorkgroup(actionRequested, nodeName,
0707: annotation, workgroupId, responsibilityDesc,
0708: ignorePreviousActions);
0709: }
0710:
0711: /**
0712: * Sends an ad hoc request to the specified user at the current active node on the document. If the document is
0713: * in a terminal state, the request will be attached to the terminal node.
0714: * @see #appSpecificRouteDocumentToUser(String, String, String, UserIdVO, String, boolean)
0715: * @see WorkflowDocumentActions#appSpecificRouteDocument(UserIdVO, RouteHeaderVO, String, String, String, ResponsiblePartyVO, String, boolean)
0716: */
0717: public void appSpecificRouteDocumentToUser(String actionRequested,
0718: String annotation, UserIdVO recipient,
0719: String responsibilityDesc, boolean ignorePreviousActions)
0720: throws WorkflowException {
0721: appSpecificRouteDocumentToUser(actionRequested, null,
0722: annotation, recipient, responsibilityDesc,
0723: ignorePreviousActions);
0724: }
0725:
0726: /**
0727: * Sends an ad hoc request to the specified user at the specified node on the document. If the document is
0728: * in a terminal state, the request will be attached to the terminal node.
0729: * @see #appSpecificRouteDocumentToUser(String, String, UserIdVO, String, boolean)
0730: * @see WorkflowDocumentActions#appSpecificRouteDocument(UserIdVO, RouteHeaderVO, String, String, String, ResponsiblePartyVO, String, boolean)
0731: */
0732: public void appSpecificRouteDocumentToUser(String actionRequested,
0733: String nodeName, String annotation, UserIdVO recipient,
0734: String responsibilityDesc, boolean ignorePreviousActions)
0735: throws WorkflowException {
0736: try {
0737: createDocumentIfNeccessary();
0738: routeHeader = getWorkflowDocumentActions()
0739: .appSpecificRouteDocument(userId, getRouteHeader(),
0740: actionRequested, nodeName, annotation,
0741: new ResponsiblePartyVO(recipient),
0742: responsibilityDesc, ignorePreviousActions);
0743: documentContentDirty = true;
0744: } catch (Exception e) {
0745: throw handleException(e);
0746: }
0747: }
0748:
0749: /**
0750: * Sends an ad hoc request to the specified workgroup at the current active node on the document. If the document is
0751: * in a terminal state, the request will be attached to the terminal node.
0752: * @see #appSpecificRouteDocumentToWorkgroup(String, String, String, WorkgroupIdVO, String, boolean)
0753: * @see WorkflowDocumentActions#appSpecificRouteDocument(UserIdVO, RouteHeaderVO, String, String, String, ResponsiblePartyVO, String, boolean)
0754: */
0755: public void appSpecificRouteDocumentToWorkgroup(
0756: String actionRequested, String annotation,
0757: WorkgroupIdVO workgroupId, String responsibilityDesc,
0758: boolean ignorePreviousActions) throws WorkflowException {
0759: appSpecificRouteDocumentToWorkgroup(actionRequested, null,
0760: annotation, workgroupId, responsibilityDesc,
0761: ignorePreviousActions);
0762: }
0763:
0764: /**
0765: * Sends an ad hoc request to the specified workgroup at the specified node on the document. If the document is
0766: * in a terminal state, the request will be attached to the terminal node.
0767: * @see #appSpecificRouteDocumentToWorkgroup(String, String, String, WorkgroupIdVO, String, boolean)
0768: * @see WorkflowDocumentActions#appSpecificRouteDocument(UserIdVO, RouteHeaderVO, String, String, String, ResponsiblePartyVO, String, boolean)
0769: */
0770: public void appSpecificRouteDocumentToWorkgroup(
0771: String actionRequested, String nodeName, String annotation,
0772: WorkgroupIdVO workgroupId, String responsibilityDesc,
0773: boolean ignorePreviousActions) throws WorkflowException {
0774: try {
0775: createDocumentIfNeccessary();
0776: routeHeader = getWorkflowDocumentActions()
0777: .appSpecificRouteDocument(userId, getRouteHeader(),
0778: actionRequested, nodeName, annotation,
0779: new ResponsiblePartyVO(workgroupId),
0780: responsibilityDesc, ignorePreviousActions);
0781: documentContentDirty = true;
0782: } catch (Exception e) {
0783: throw handleException(e);
0784: }
0785: }
0786:
0787: /**
0788: * Revokes AdHoc request(s) according to the given AdHocRevokeVO which is passed in.
0789: *
0790: * If a specific action request ID is specified on the revoke bean, and that ID is not a valid ID, this method should throw a
0791: * WorkflowException.
0792: * @param revoke AdHocRevokeVO
0793: * @param annotation message to note for this action
0794: * @throws WorkflowException if an error occurs revoking adhoc requests
0795: * @see WorkflowDocumentActions#revokeAdHocRequests(UserIdVO, RouteHeaderVO, AdHocRevokeVO, String)
0796: */
0797: public void revokeAdHocRequests(AdHocRevokeVO revoke,
0798: String annotation) throws WorkflowException {
0799: if (getRouteHeader().getRouteHeaderId() == null) {
0800: throw new WorkflowException(
0801: "Can't revoke request, the workflow document has not yet been created!");
0802: }
0803: try {
0804: createDocumentIfNeccessary();
0805: routeHeader = getWorkflowDocumentActions()
0806: .revokeAdHocRequests(userId, getRouteHeader(),
0807: revoke, annotation);
0808: documentContentDirty = true;
0809: } catch (Exception e) {
0810: throw handleException(e);
0811: }
0812: }
0813:
0814: /**
0815: * Sets the title of the document, empty string if null is specified.
0816: * @param title title of the document to set, or null
0817: */
0818: // WorkflowException is declared but not thrown...
0819: public void setTitle(String title) throws WorkflowException {
0820: if (title == null) {
0821: title = "";
0822: }
0823: if (title.length() > EdenConstants.TITLE_MAX_LENGTH) {
0824: title = title.substring(0, EdenConstants.TITLE_MAX_LENGTH);
0825: }
0826: getRouteHeader().setDocTitle(title);
0827: }
0828:
0829: /**
0830: * Returns the document type of the workflow document
0831: * @return the document type of the workflow document
0832: * @throws RuntimeException if document does not exist (is not yet created)
0833: * @see RouteHeaderVO#getDocTypeName()
0834: */
0835: public String getDocumentType() {
0836: if (getRouteHeader() == null) {
0837: // HACK: FIXME: we should probably proscribe, or at least handle consistently, these corner cases
0838: // NPEs are not nice
0839: throw new RuntimeException("No such document!");
0840: }
0841: return getRouteHeader().getDocTypeName();
0842: }
0843:
0844: /**
0845: * Returns whether an acknowledge is requested of the user for this document. This is
0846: * a convenience method that delegates to {@link RouteHeaderVO#isAckRequested()}.
0847: * @return whether an acknowledge is requested of the user for this document
0848: * @see RouteHeaderVO#isAckRequested()
0849: */
0850: public boolean isAcknowledgeRequested() {
0851: return getRouteHeader().isAckRequested();
0852: }
0853:
0854: /**
0855: * Returns whether an approval is requested of the user for this document. This is
0856: * a convenience method that delegates to {@link RouteHeaderVO#isApproveRequested()}.
0857: * @return whether an approval is requested of the user for this document
0858: * @see RouteHeaderVO#isApproveRequested()
0859: */
0860: public boolean isApprovalRequested() {
0861: return getRouteHeader().isApproveRequested();
0862: }
0863:
0864: /**
0865: * Returns whether a completion is requested of the user for this document. This is
0866: * a convenience method that delegates to {@link RouteHeaderVO#isCompleteRequested()}.
0867: * @return whether an approval is requested of the user for this document
0868: * @see RouteHeaderVO#isCompleteRequested()
0869: */
0870: public boolean isCompletionRequested() {
0871: return getRouteHeader().isCompleteRequested();
0872: }
0873:
0874: /**
0875: * Returns whether an FYI is requested of the user for this document. This is
0876: * a convenience method that delegates to {@link RouteHeaderVO#isFyiRequested()}.
0877: * @return whether an FYI is requested of the user for this document
0878: * @see RouteHeaderVO#isFyiRequested()
0879: */
0880: public boolean isFYIRequested() {
0881: return getRouteHeader().isFyiRequested();
0882: }
0883:
0884: /**
0885: * Returns whether the user can blanket approve the document
0886: * @return whether the user can blanket approve the document
0887: * @see RouteHeaderVO#getValidActions()
0888: */
0889: public boolean isBlanketApproveCapable() {
0890: // TODO delyea - refactor this to take into account non-initiator owned documents
0891: return getRouteHeader().getValidActions().contains(
0892: EdenConstants.ACTION_TAKEN_BLANKET_APPROVE_CD)
0893: && (isCompletionRequested() || isApprovalRequested() || stateIsInitiated());
0894: }
0895:
0896: /**
0897: * Returns whether the specified action code is valid for the current user and document
0898: * @return whether the user can blanket approve the document
0899: * @see RouteHeaderVO#getValidActions()
0900: */
0901: public boolean isActionCodeValidForDocument(String actionTakenCode) {
0902: return getRouteHeader().getValidActions().contains(
0903: actionTakenCode);
0904: }
0905:
0906: /**
0907: * Performs the 'super-user-approve' action on the document this WorkflowDocument represents. If this is a new document,
0908: * the document is created first.
0909: * @param annotation the message to log for the action
0910: * @throws WorkflowException in case an error occurs super-user-approve-ing the document
0911: * @see WorkflowDocumentActions#superUserApprove(UserIdVO, RouteHeaderVO, String)
0912: */
0913: public void super UserApprove(String annotation)
0914: throws WorkflowException {
0915: try {
0916: createDocumentIfNeccessary();
0917: routeHeader = getWorkflowDocumentActions()
0918: .super UserApprove(getUserId(), getRouteHeader(),
0919: annotation);
0920: documentContentDirty = true;
0921: } catch (Exception e) {
0922: throw handleException(e);
0923: }
0924: }
0925:
0926: public void super UserActionRequestApprove(Long actionRequestId,
0927: String annotation) throws WorkflowException {
0928: try {
0929: createDocumentIfNeccessary();
0930: routeHeader = getWorkflowDocumentActions()
0931: .super UserActionRequestApprove(getUserId(),
0932: getRouteHeader(), actionRequestId,
0933: annotation);
0934: documentContentDirty = true;
0935: } catch (Exception e) {
0936: throw handleException(e);
0937: }
0938: }
0939:
0940: /**
0941: * Performs the 'super-user-disapprove' action on the document this WorkflowDocument represents. If this is a new document,
0942: * the document is created first.
0943: * @param annotation the message to log for the action
0944: * @throws WorkflowException in case an error occurs super-user-disapprove-ing the document
0945: * @see WorkflowDocumentActions#superUserDisapprove(UserIdVO, RouteHeaderVO, String)
0946: */
0947: public void super UserDisapprove(String annotation)
0948: throws WorkflowException {
0949: try {
0950: createDocumentIfNeccessary();
0951: routeHeader = getWorkflowDocumentActions()
0952: .super UserDisapprove(getUserId(), getRouteHeader(),
0953: annotation);
0954: documentContentDirty = true;
0955: } catch (Exception e) {
0956: throw handleException(e);
0957: }
0958: }
0959:
0960: /**
0961: * Performs the 'super-user-cancel' action on the document this WorkflowDocument represents. If this is a new document,
0962: * the document is created first.
0963: * @param annotation the message to log for the action
0964: * @throws WorkflowException in case an error occurs super-user-cancel-ing the document
0965: * @see WorkflowDocumentActions#superUserCancel(UserIdVO, RouteHeaderVO, String)
0966: */
0967: public void super UserCancel(String annotation)
0968: throws WorkflowException {
0969: try {
0970: createDocumentIfNeccessary();
0971: routeHeader = getWorkflowDocumentActions().super UserCancel(
0972: getUserId(), getRouteHeader(), annotation);
0973: documentContentDirty = true;
0974: } catch (Exception e) {
0975: throw handleException(e);
0976: }
0977: }
0978:
0979: /**
0980: * Returns whether the user is a super user on this document
0981: * @return whether the user is a super user on this document
0982: * @throws WorkflowException if an error occurs determining whether the user is a super user on this document
0983: * @see WorkflowUtility#isSuperUserForDocumentType(UserIdVO, Long)
0984: */
0985: public boolean isSuperUser() throws WorkflowException {
0986: try {
0987: createDocumentIfNeccessary();
0988: return getWorkflowUtility().isSuperUserForDocumentType(
0989: getUserId(), getRouteHeader().getDocTypeId());
0990: } catch (Exception e) {
0991: throw handleException(e);
0992: }
0993: }
0994:
0995: /**
0996: * Returns whether the user passed into WorkflowDocument at instantiation can route
0997: * the document.
0998: * @return if user passed into WorkflowDocument at instantiation can route
0999: * the document.
1000: */
1001: // TODO delyea - Should this be removed due to policies and valid actions adjustments?
1002: public boolean isRouteCapable() {
1003: UserIdVO userId = getUserId();
1004: if (userId instanceof NetworkIdVO) {
1005: return ((NetworkIdVO) userId).getNetworkId().equals(
1006: getRouteHeader().getInitiator().getNetworkId())
1007: && stateIsInitiated();
1008: } else if (userId instanceof UuIdVO) {
1009: return ((UuIdVO) userId).getUuId().equals(
1010: getRouteHeader().getInitiator().getUuId())
1011: && stateIsInitiated();
1012: } else if (userId instanceof EmplIdVO) {
1013: return ((EmplIdVO) userId).getEmplId().equals(
1014: getRouteHeader().getInitiator().getEmplId())
1015: && stateIsInitiated();
1016: } else if (userId instanceof WorkflowIdVO) {
1017: return ((WorkflowIdVO) userId).getWorkflowId().equals(
1018: getRouteHeader().getInitiator().getWorkflowId())
1019: && stateIsInitiated();
1020: }
1021: throw new UnsupportedOperationException(
1022: "UserId type not yet supported on this method.");
1023: }
1024:
1025: /**
1026: * Performs the 'clearFYI' action on the document this WorkflowDocument represents. If this is a new document,
1027: * the document is created first.
1028: * @param annotation the message to log for the action
1029: * @throws WorkflowException in case an error occurs clearing FYI on the document
1030: * @see WorkflowDocumentActions#clearFYIDocument(UserIdVO, RouteHeaderVO)
1031: */
1032: public void clearFYI() throws WorkflowException {
1033: try {
1034: createDocumentIfNeccessary();
1035: getWorkflowDocumentActions().clearFYIDocument(userId,
1036: getRouteHeader());
1037: documentContentDirty = true;
1038: } catch (Exception e) {
1039: throw handleException(e);
1040: }
1041: }
1042:
1043: /**
1044: * Performs the 'complete' action on the document this WorkflowDocument represents. If this is a new document,
1045: * the document is created first.
1046: * @param annotation the message to log for the action
1047: * @throws WorkflowException in case an error occurs clearing completing the document
1048: * @see WorkflowDocumentActions#completeDocument(UserIdVO, RouteHeaderVO, String)
1049: */
1050: public void complete(String annotation) throws WorkflowException {
1051: try {
1052: createDocumentIfNeccessary();
1053: routeHeader = getWorkflowDocumentActions()
1054: .completeDocument(userId, getRouteHeader(),
1055: annotation);
1056: documentContentDirty = true;
1057: } catch (Exception e) {
1058: throw handleException(e);
1059: }
1060: }
1061:
1062: /**
1063: * Performs the 'logDocumentAction' action on the document this WorkflowDocument represents. If this is a new document,
1064: * the document is created first. The 'logDocumentAction' simply logs a message on the document.
1065: * @param annotation the message to log for the action
1066: * @throws WorkflowException in case an error occurs logging a document action on the document
1067: * @see WorkflowDocumentActions#logDocumentAction(UserIdVO, RouteHeaderVO, String)
1068: */
1069: public void logDocumentAction(String annotation)
1070: throws WorkflowException {
1071: try {
1072: createDocumentIfNeccessary();
1073: getWorkflowDocumentActions().logDocumentAction(userId,
1074: getRouteHeader(), annotation);
1075: documentContentDirty = true;
1076: } catch (Exception e) {
1077: throw handleException(e);
1078: }
1079: }
1080:
1081: /**
1082: * Indicates if the document is in the initiated state or not.
1083: *
1084: * @return true if in the specified state
1085: */
1086: public boolean stateIsInitiated() {
1087: return EdenConstants.ROUTE_HEADER_INITIATED_CD
1088: .equals(getRouteHeader().getDocRouteStatus());
1089: }
1090:
1091: /**
1092: * Indicates if the document is in the saved state or not.
1093: *
1094: * @return true if in the specified state
1095: */
1096: public boolean stateIsSaved() {
1097: return EdenConstants.ROUTE_HEADER_SAVED_CD
1098: .equals(getRouteHeader().getDocRouteStatus());
1099: }
1100:
1101: /**
1102: * Indicates if the document is in the enroute state or not.
1103: *
1104: * @return true if in the specified state
1105: */
1106: public boolean stateIsEnroute() {
1107: return EdenConstants.ROUTE_HEADER_ENROUTE_CD
1108: .equals(getRouteHeader().getDocRouteStatus());
1109: }
1110:
1111: /**
1112: * Indicates if the document is in the exception state or not.
1113: *
1114: * @return true if in the specified state
1115: */
1116: public boolean stateIsException() {
1117: return EdenConstants.ROUTE_HEADER_EXCEPTION_CD
1118: .equals(getRouteHeader().getDocRouteStatus());
1119: }
1120:
1121: /**
1122: * Indicates if the document is in the canceled state or not.
1123: *
1124: * @return true if in the specified state
1125: */
1126: public boolean stateIsCanceled() {
1127: return EdenConstants.ROUTE_HEADER_CANCEL_CD
1128: .equals(getRouteHeader().getDocRouteStatus());
1129: }
1130:
1131: /**
1132: * Indicates if the document is in the disapproved state or not.
1133: *
1134: * @return true if in the specified state
1135: */
1136: public boolean stateIsDisapproved() {
1137: return EdenConstants.ROUTE_HEADER_DISAPPROVED_CD
1138: .equals(getRouteHeader().getDocRouteStatus());
1139: }
1140:
1141: /**
1142: * Indicates if the document is in the approved state or not. Will answer true is document is in Processed or Finalized state.
1143: *
1144: * @return true if in the specified state
1145: */
1146: public boolean stateIsApproved() {
1147: return EdenConstants.ROUTE_HEADER_APPROVED_CD
1148: .equals(getRouteHeader().getDocRouteStatus())
1149: || stateIsProcessed() || stateIsFinal();
1150: }
1151:
1152: /**
1153: * Indicates if the document is in the processed state or not.
1154: *
1155: * @return true if in the specified state
1156: */
1157: public boolean stateIsProcessed() {
1158: return EdenConstants.ROUTE_HEADER_PROCESSED_CD
1159: .equals(getRouteHeader().getDocRouteStatus());
1160: }
1161:
1162: /**
1163: * Indicates if the document is in the final state or not.
1164: *
1165: * @return true if in the specified state
1166: */
1167: public boolean stateIsFinal() {
1168: return EdenConstants.ROUTE_HEADER_FINAL_CD
1169: .equals(getRouteHeader().getDocRouteStatus());
1170: }
1171:
1172: /**
1173: * Returns the display value of the current document status
1174: * @return the display value of the current document status
1175: */
1176: public String getStatusDisplayValue() {
1177: return (String) EdenConstants.DOCUMENT_STATUSES
1178: .get(getRouteHeader().getDocRouteStatus());
1179: }
1180:
1181: /**
1182: * Returns the userId with which this WorkflowDocument was constructed
1183: * @return the userId with which this WorkflowDocument was constructed
1184: */
1185: public UserIdVO getUserId() {
1186: return userId;
1187: }
1188:
1189: /**
1190: * Sets the userId under which actions against this document should be taken
1191: * @param userId userId under which actions against this document should be taken
1192: */
1193: public void setUserId(UserIdVO userId) {
1194: this .userId = userId;
1195: }
1196:
1197: /**
1198: * Checks if the document has been created or not (i.e. has a route header id or not) and issues
1199: * a call to the server to create the document if it has not yet been created.
1200: *
1201: * Also checks if the document content has been updated and saves it if it has.
1202: */
1203: private void createDocumentIfNeccessary() throws RemoteException,
1204: WorkflowException {
1205: if (getRouteHeader().getRouteHeaderId() == null) {
1206: routeHeader = getWorkflowDocumentActions().createDocument(
1207: userId, getRouteHeader());
1208: }
1209: if (documentContent != null && documentContent.isModified()) {
1210: saveDocumentContent(documentContent);
1211: }
1212: }
1213:
1214: /**
1215: * Helper to prevent us from needlessly wrapping a WorkflowException in another WorkflowException.
1216: */
1217: private WorkflowException handleException(Exception e) {
1218: if (e instanceof WorkflowException) {
1219: return (WorkflowException) e;
1220: }
1221: return new WorkflowException(e);
1222: }
1223:
1224: /**
1225: * Like handleException except it returns a RuntimeException.
1226: */
1227: private RuntimeException handleExceptionAsRuntime(Exception e) {
1228: if (e instanceof RuntimeException) {
1229: return (RuntimeException) e;
1230: }
1231: return new WorkflowRuntimeException(e);
1232: }
1233:
1234: // WORKFLOW 2.1: new methods
1235:
1236: /**
1237: * Performs the 'blanketApprove' action on the document this WorkflowDocument represents. If this is a new document,
1238: * the document is created first.
1239: * @param annotation the message to log for the action
1240: * @param nodeName the extent to which to blanket approve; blanket approval will stop at this node
1241: * @throws WorkflowException in case an error occurs blanket-approving the document
1242: * @see WorkflowDocumentActions#blanketApprovalToNodes(UserIdVO, RouteHeaderVO, String, String[])
1243: */
1244: public void blanketApprove(String annotation, String nodeName)
1245: throws WorkflowException {
1246: blanketApprove(annotation, (nodeName == null ? null
1247: : new String[] { nodeName }));
1248: }
1249:
1250: /**
1251: * Performs the 'blanketApprove' action on the document this WorkflowDocument represents. If this is a new document,
1252: * the document is created first.
1253: * @param annotation the message to log for the action
1254: * @param nodeNames the nodes at which blanket approval will stop (in case the blanket approval traverses a split, in which case there may be multiple "active" nodes)
1255: * @throws WorkflowException in case an error occurs blanket-approving the document
1256: * @see WorkflowDocumentActions#blanketApprovalToNodes(UserIdVO, RouteHeaderVO, String, String[])
1257: */
1258: public void blanketApprove(String annotation, String[] nodeNames)
1259: throws WorkflowException {
1260: try {
1261: createDocumentIfNeccessary();
1262: routeHeader = getWorkflowDocumentActions()
1263: .blanketApprovalToNodes(userId, getRouteHeader(),
1264: annotation, nodeNames);
1265: documentContentDirty = true;
1266: } catch (Exception e) {
1267: throw handleException(e);
1268: }
1269: }
1270:
1271: /**
1272: * The user taking action removes the action items for this workgroup and document from all other
1273: * group members' action lists. If this is a new document, the document is created first.
1274: *
1275: * @param annotation the message to log for the action
1276: * @param workgroupId the workgroup on which to take authority
1277: * @throws WorkflowException user taking action is not in workgroup
1278: * @see WorkflowDocumentActions#takeWorkgroupAuthority(UserIdVO, RouteHeaderVO, WorkgroupIdVO, String)
1279: */
1280: public void takeWorkgroupAuthority(String annotation,
1281: WorkgroupIdVO workgroupId) throws WorkflowException {
1282: try {
1283: createDocumentIfNeccessary();
1284: routeHeader = getWorkflowDocumentActions()
1285: .takeWorkgroupAuthority(userId, getRouteHeader(),
1286: workgroupId, annotation);
1287: documentContentDirty = true;
1288: } catch (Exception e) {
1289: throw handleException(e);
1290: }
1291: }
1292:
1293: /**
1294: * The user that took the group authority is putting the action items back in the other users action lists.
1295: * If this is a new document, the document is created first.
1296: *
1297: * @param annotation the message to log for the action
1298: * @param workgroupId the workgroup on which to take authority
1299: * @throws WorkflowException user taking action is not in workgroup or did not take workgroup authority
1300: */
1301: public void releaseWorkgroupAuthority(String annotation,
1302: WorkgroupIdVO workgroupId) throws WorkflowException {
1303: try {
1304: createDocumentIfNeccessary();
1305: routeHeader = getWorkflowDocumentActions()
1306: .releaseWorkgroupAuthority(userId,
1307: getRouteHeader(), workgroupId, annotation);
1308: documentContentDirty = true;
1309: } catch (Exception e) {
1310: throw handleException(e);
1311: }
1312: }
1313:
1314: /**
1315: * Returns names of all active nodes the document is currently at.
1316: *
1317: * @return names of all active nodes the document is currently at.
1318: * @throws WorkflowException if there is an error obtaining the currently active nodes on the document
1319: * @see WorkflowUtility#getActiveNodeInstances(Long)
1320: */
1321: public String[] getNodeNames() throws WorkflowException {
1322: try {
1323: RouteNodeInstanceVO[] activeNodeInstances = getWorkflowUtility()
1324: .getActiveNodeInstances(getRouteHeaderId());
1325: String[] nodeNames = new String[(activeNodeInstances == null ? 0
1326: : activeNodeInstances.length)];
1327: for (int index = 0; index < activeNodeInstances.length; index++) {
1328: nodeNames[index] = activeNodeInstances[index].getName();
1329: }
1330: return nodeNames;
1331: } catch (Exception e) {
1332: throw handleException(e);
1333: }
1334: }
1335:
1336: /**
1337: * Performs the 'returnToPrevious' action on the document this WorkflowDocument represents. If this is a new document,
1338: * the document is created first.
1339: * @param annotation the message to log for the action
1340: * @param nodeName the node to return to
1341: * @throws WorkflowException in case an error occurs returning to previous node
1342: * @see WorkflowDocumentActions#returnDocumentToPreviousNode(UserIdVO, RouteHeaderVO, ReturnPointVO, String)
1343: */
1344: public void returnToPreviousNode(String annotation, String nodeName)
1345: throws WorkflowException {
1346: ReturnPointVO returnPoint = new ReturnPointVO(nodeName);
1347: returnToPreviousNode(annotation, returnPoint);
1348: }
1349:
1350: /**
1351: * Performs the 'returnToPrevious' action on the document this WorkflowDocument represents. If this is a new document,
1352: * the document is created first.
1353: * @param annotation the message to log for the action
1354: * @param ReturnPointVO the node to return to
1355: * @throws WorkflowException in case an error occurs returning to previous node
1356: * @see WorkflowDocumentActions#returnDocumentToPreviousNode(UserIdVO, RouteHeaderVO, ReturnPointVO, String)
1357: */
1358: public void returnToPreviousNode(String annotation,
1359: ReturnPointVO returnPoint) throws WorkflowException {
1360: try {
1361: createDocumentIfNeccessary();
1362: routeHeader = getWorkflowDocumentActions()
1363: .returnDocumentToPreviousNode(userId,
1364: getRouteHeader(), returnPoint, annotation);
1365: documentContentDirty = true;
1366: } catch (Exception e) {
1367: throw handleException(e);
1368: }
1369: }
1370:
1371: /**
1372: * Moves the document from a current node in it's route to another node. If this is a new document,
1373: * the document is created first.
1374: * @param MovePointVO VO representing the node at which to start, and the number of steps to move (negative steps is reverse)
1375: * @param annotation the message to log for the action
1376: * @throws WorkflowException in case an error occurs moving the document
1377: * @see WorkflowDocumentActions#moveDocument(UserIdVO, RouteHeaderVO, MovePointVO, String)
1378: */
1379: public void moveDocument(MovePointVO movePoint, String annotation)
1380: throws WorkflowException {
1381: try {
1382: createDocumentIfNeccessary();
1383: routeHeader = getWorkflowDocumentActions().moveDocument(
1384: userId, getRouteHeader(), movePoint, annotation);
1385: documentContentDirty = true;
1386: } catch (Exception e) {
1387: throw handleException(e);
1388: }
1389: }
1390:
1391: /**
1392: * Returns the route node instances that have been created so far during the life of this document. This includes
1393: * all previous instances which have already been processed and are no longer active.
1394: * @return the route node instances that have been created so far during the life of this document
1395: * @throws WorkflowException if there is an error getting the route node instances for the document
1396: * @see WorkflowUtility#getDocumentRouteNodeInstances(Long)
1397: */
1398: public RouteNodeInstanceVO[] getRouteNodeInstances()
1399: throws WorkflowException {
1400: try {
1401: return getWorkflowUtility().getDocumentRouteNodeInstances(
1402: getRouteHeaderId());
1403: } catch (Exception e) {
1404: throw handleException(e);
1405: }
1406: }
1407:
1408: /**
1409: * Returns Array of Route Nodes Names that can be safely returned to using the 'returnToPreviousXXX' methods.
1410: * Names are sorted in reverse chronological order.
1411: *
1412: * @return array of Route Nodes Names that can be safely returned to using the 'returnToPreviousXXX' methods
1413: * @throws WorkflowException if an error occurs obtaining the names of the previous route nodes for this document
1414: * @see WorkflowUtility#getPreviousRouteNodeNames(Long)
1415: */
1416: public String[] getPreviousNodeNames() throws WorkflowException {
1417: try {
1418: return getWorkflowUtility().getPreviousRouteNodeNames(
1419: getRouteHeaderId());
1420: } catch (Exception e) {
1421: throw new WorkflowException(e);
1422: }
1423: }
1424:
1425: /**
1426: * Returns a document detail VO representing the route header along with action requests, actions taken,
1427: * and route node instances.
1428: * @return Returns a document detail VO representing the route header along with action requests, actions taken, and route node instances.
1429: * @throws WorkflowException
1430: */
1431: public DocumentDetailVO getDetail() throws WorkflowException {
1432: try {
1433: return getWorkflowUtility().getDocumentDetail(
1434: getRouteHeaderId());
1435: } catch (Exception e) {
1436: throw handleException(e);
1437: }
1438: }
1439:
1440: /**
1441: * Saves the given DocumentContentVO for this document.
1442: * @param documentContent document content VO to store for this document
1443: * @since 2.3
1444: * @see WorkflowDocumentActions#saveDocumentContent(DocumentContentVO)
1445: */
1446: public DocumentContentVO saveDocumentContent(
1447: DocumentContentVO documentContent) throws WorkflowException {
1448: try {
1449: if (documentContent.getRouteHeaderId() == null) {
1450: throw new WorkflowException(
1451: "Document Content does not have a valid document ID.");
1452: }
1453: // important to check directly against getRouteHeader().getRouteHeaderId() instead of just getRouteHeaderId() because saveDocumentContent
1454: // is called from createDocumentIfNeccessary which is called from getRouteHeaderId(). If that method was used, we would have an infinite loop.
1455: if (!documentContent.getRouteHeaderId().equals(
1456: getRouteHeader().getRouteHeaderId())) {
1457: throw new WorkflowException(
1458: "Attempted to save content on this document with an invalid document id of "
1459: + documentContent.getRouteHeaderId());
1460: }
1461: DocumentContentVO newDocumentContent = getWorkflowDocumentActions()
1462: .saveDocumentContent(documentContent);
1463: this .documentContent = new ModifiableDocumentContentVO(
1464: newDocumentContent);
1465: documentContentDirty = false;
1466: return this .documentContent;
1467: } catch (Exception e) {
1468: throw handleException(e);
1469: }
1470: }
1471:
1472: // DEPRECATED: as of Workflow 2.0
1473:
1474: /**
1475: * @deprecated use getRouteHeader.getInitiator
1476: */
1477: public String getInitiatorNetworkId() {
1478: if (routeHeader.getInitiator() != null) {
1479: return routeHeader.getInitiator().getNetworkId();
1480: } else {
1481: return "";
1482: }
1483: }
1484:
1485: // DEPRECATED: as of Workflow 2.1
1486:
1487: /**
1488: * @deprecated use blanketApprove(String annotation, String nodeName) instead
1489: */
1490: public void blanketApprove(String annotation, Integer routeLevel)
1491: throws WorkflowException {
1492: try {
1493: createDocumentIfNeccessary();
1494: routeHeader = getWorkflowDocumentActions().blanketApproval(
1495: userId, getRouteHeader(), annotation, routeLevel);
1496: documentContentDirty = true;
1497: } catch (Exception e) {
1498: throw handleException(e);
1499: }
1500: }
1501:
1502: /**
1503: * @deprecated use getNodeNames() instead
1504: */
1505: public Integer getDocRouteLevel() {
1506: return routeHeader.getDocRouteLevel();
1507: }
1508:
1509: /**
1510: * TODO this method still needs to be updated to work properly for Workflow 2.1
1511: * it would probably be easier to just put this info on bean from the server
1512: *
1513: * @deprecated use getNodeNames() instead
1514: */
1515: public String getDocRouteLevelName() throws WorkflowException {
1516: try {
1517: if (getDocumentType() == null) {
1518: throw new DocumentTypeNotFoundException(
1519: "Document Type Name is null");
1520: }
1521: RouteTemplateEntryVO[] routeLevels = getWorkflowUtility()
1522: .getDocRoute(getDocumentType());
1523: for (int i = 0; i < routeLevels.length; i++) {
1524: if (routeLevels[i].getRouteLevel().equals(
1525: getDocRouteLevel())) {
1526: return routeLevels[i].getRouteLevelName();
1527: }
1528: }
1529: } catch (Exception e) {
1530: throw handleException(e);
1531: }
1532: throw new WorkflowException("Did not find a route level");
1533: }
1534:
1535: /**
1536: * TODO this method still needs to be updated to work properly for Workflow 2.1
1537: * it would probably be easier to just put this info on bean from the server
1538: *
1539: * @deprecated use getRouteMethodNames instead
1540: */
1541: public String getRouteMethodName() throws WorkflowException {
1542: if (getDocumentType() == null) {
1543: throw new WorkflowException("Document Type Name is null");
1544: }
1545:
1546: try {
1547: RouteTemplateEntryVO[] routeLevels = getWorkflowUtility()
1548: .getDocRoute(getDocumentType());
1549: for (int i = 0; i < routeLevels.length; i++) {
1550: if (routeLevels[i].getRouteLevel().equals(
1551: getDocRouteLevel())) {
1552: return routeLevels[i].getRouteMethodName();
1553: }
1554: }
1555: } catch (Exception e) {
1556: throw handleException(e);
1557: }
1558:
1559: throw new WorkflowException("Did not find a route level");
1560: }
1561:
1562: /**
1563: * @deprecated use returnToPreviousNode(String annotation, String nodeName) instead
1564: */
1565: public void returnToPreviousRouteLevel(String annotation,
1566: Integer destRouteLevel) throws WorkflowException {
1567: try {
1568: createDocumentIfNeccessary();
1569: getWorkflowDocumentActions()
1570: .returnDocumentToPreviousRouteLevel(userId,
1571: getRouteHeader(), destRouteLevel,
1572: annotation);
1573: documentContentDirty = true;
1574: } catch (Exception e) {
1575: throw handleException(e);
1576: }
1577: }
1578:
1579: /**
1580: * Returns a list of NoteVO representing the notes on the document
1581: * @return a list of NoteVO representing the notes on the document
1582: * @see RouteHeaderVO#getNotes()
1583: */
1584: public List<NoteVO> getNoteList() {
1585: List<NoteVO> notesList = new ArrayList<NoteVO>();
1586: NoteVO[] notes = routeHeader.getNotes();
1587: if (notes != null) {
1588: for (int i = 0; i < notes.length; i++) {
1589: if (!isDeletedNote(notes[i])) {
1590: notesList.add(notes[i]);
1591: }
1592: }
1593: }
1594: return notesList;
1595: }
1596:
1597: /**
1598: * Deletes a note from the document. The deletion is deferred until the next time the document is committed (via an action).
1599: * @param noteVO the note to remove from the document
1600: */
1601: public void deleteNote(NoteVO noteVO) {
1602: if (noteVO != null && noteVO.getNoteId() != null) {
1603: NoteVO noteToDelete = new NoteVO();
1604: noteToDelete.setNoteId(new Long(noteVO.getNoteId()
1605: .longValue()));
1606: /*noteToDelete.setRouteHeaderId(noteVO.getRouteHeaderId());
1607: noteToDelete.setNoteAuthorWorkflowId(noteVO.getNoteAuthorWorkflowId());
1608: noteToDelete.setNoteCreateDate(noteVO.getNoteCreateDate());
1609: noteToDelete.setNoteText(noteVO.getNoteText());
1610: noteToDelete.setLockVerNbr(noteVO.getLockVerNbr());*/
1611: increaseNotesToDeleteArraySizeByOne();
1612: routeHeader.getNotesToDelete()[routeHeader
1613: .getNotesToDelete().length - 1] = noteToDelete;
1614: }
1615: }
1616:
1617: /**
1618: * Updates the note of the same note id, on the document. The update is deferred until the next time the document is committed (via an action).
1619: * @param noteVO the note to update
1620: */
1621: public void updateNote(NoteVO noteVO) {
1622: boolean isUpdateNote = false;
1623: if (noteVO != null) {
1624: NoteVO[] notes = routeHeader.getNotes();
1625: NoteVO copyNote = new NoteVO();
1626: if (noteVO.getNoteId() != null) {
1627: copyNote.setNoteId(new Long(noteVO.getNoteId()
1628: .longValue()));
1629: }
1630:
1631: if (noteVO.getRouteHeaderId() != null) {
1632: copyNote.setRouteHeaderId(new Long(noteVO
1633: .getRouteHeaderId().longValue()));
1634: } else {
1635: copyNote.setRouteHeaderId(routeHeader
1636: .getRouteHeaderId());
1637: }
1638:
1639: if (noteVO.getNoteAuthorWorkflowId() != null) {
1640: copyNote.setNoteAuthorWorkflowId(new String(noteVO
1641: .getNoteAuthorWorkflowId()));
1642: } else {
1643: copyNote.setNoteAuthorWorkflowId(userId.toString());
1644: }
1645:
1646: if (noteVO.getNoteCreateDate() != null) {
1647: Calendar cal = Calendar.getInstance();
1648: cal.setTimeInMillis(noteVO.getNoteCreateDate()
1649: .getTimeInMillis());
1650: copyNote.setNoteCreateDate(cal);
1651: } else {
1652: copyNote.setNoteCreateDate(Calendar.getInstance());
1653: }
1654:
1655: if (noteVO.getNoteText() != null) {
1656: copyNote.setNoteText(new String(noteVO.getNoteText()));
1657: }
1658: if (noteVO.getLockVerNbr() != null) {
1659: copyNote.setLockVerNbr(new Integer(noteVO
1660: .getLockVerNbr().intValue()));
1661: }
1662: if (notes != null) {
1663: for (int i = 0; i < notes.length; i++) {
1664: if (notes[i].getNoteId() != null
1665: && notes[i].getNoteId().equals(
1666: copyNote.getNoteId())) {
1667: notes[i] = copyNote;
1668: isUpdateNote = true;
1669: break;
1670: }
1671: }
1672: }
1673: // add new note to the notes array
1674: if (!isUpdateNote) {
1675: copyNote.setNoteId(null);
1676: increaseNotesArraySizeByOne();
1677: routeHeader.getNotes()[routeHeader.getNotes().length - 1] = copyNote;
1678: }
1679: }
1680: }
1681:
1682: /**
1683: * Sets a variable on the document. The assignment is deferred until the next time the document is committed (via an action).
1684: * @param name name of the variable
1685: * @param value value of the variable
1686: */
1687: public void setVariable(String name, String value)
1688: throws WorkflowException {
1689: try {
1690: createDocumentIfNeccessary();
1691: } catch (Exception e) {
1692: throw handleException(e);
1693: }
1694: getRouteHeader().setVariable(name, value);
1695: }
1696:
1697: /**
1698: * Gets the value of a variable on the document, creating the document first if it does not exist.
1699: * @param name variable name
1700: * @return variable value
1701: */
1702: public String getVariable(String name) throws WorkflowException {
1703: try {
1704: createDocumentIfNeccessary();
1705: } catch (Exception e) {
1706: throw handleException(e);
1707: }
1708: return getRouteHeader().getVariable(name);
1709: }
1710:
1711: /**
1712: * Deletes the note of with the same id as that of the argument on the document.
1713: * @param noteVO the note to test for deletion
1714: * @return whether the note is already marked for deletion.
1715: */
1716: private boolean isDeletedNote(NoteVO noteVO) {
1717: NoteVO[] notesToDelete = routeHeader.getNotesToDelete();
1718: if (notesToDelete != null) {
1719: for (int i = 0; i < notesToDelete.length; i++) {
1720: if (notesToDelete[i].getNoteId().equals(
1721: noteVO.getNoteId())) {
1722: return true;
1723: }
1724: }
1725: }
1726: return false;
1727: }
1728:
1729: /**
1730: * Increases the size of the routeHeader notes VO array
1731: */
1732: private void increaseNotesArraySizeByOne() {
1733: NoteVO[] tempArray;
1734: NoteVO[] notes = routeHeader.getNotes();
1735: if (notes == null) {
1736: tempArray = new NoteVO[1];
1737: } else {
1738: tempArray = new NoteVO[notes.length + 1];
1739: for (int i = 0; i < notes.length; i++) {
1740: tempArray[i] = notes[i];
1741: }
1742: }
1743: routeHeader.setNotes(tempArray);
1744: }
1745:
1746: /**
1747: * Increases the size of the routeHeader notesToDelete VO array
1748: */
1749: private void increaseNotesToDeleteArraySizeByOne() {
1750: NoteVO[] tempArray;
1751: NoteVO[] notesToDelete = routeHeader.getNotesToDelete();
1752: if (notesToDelete == null) {
1753: tempArray = new NoteVO[1];
1754: } else {
1755: tempArray = new NoteVO[notesToDelete.length + 1];
1756: for (int i = 0; i < notesToDelete.length; i++) {
1757: tempArray[i] = notesToDelete[i];
1758: }
1759: }
1760: routeHeader.setNotesToDelete(tempArray);
1761: }
1762: }
|