Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
WorkflowDocument |
|
| 1.7777777777777777;1.778 |
1 | /* | |
2 | * Copyright 2005-2007 The Kuali Foundation | |
3 | * | |
4 | * | |
5 | * Licensed under the Educational Community License, Version 2.0 (the "License"); | |
6 | * you may not use this file except in compliance with the License. | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.opensource.org/licenses/ecl2.php | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | package org.kuali.rice.kew.service; | |
18 | ||
19 | import java.sql.Timestamp; | |
20 | import java.util.ArrayList; | |
21 | import java.util.Calendar; | |
22 | import java.util.List; | |
23 | ||
24 | import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; | |
25 | import org.kuali.rice.kew.api.WorkflowRuntimeException; | |
26 | import org.kuali.rice.kew.dto.ActionRequestDTO; | |
27 | import org.kuali.rice.kew.dto.ActionTakenDTO; | |
28 | import org.kuali.rice.kew.dto.AdHocRevokeDTO; | |
29 | import org.kuali.rice.kew.dto.DocumentContentDTO; | |
30 | import org.kuali.rice.kew.dto.DocumentDetailDTO; | |
31 | import org.kuali.rice.kew.dto.DocumentLinkDTO; | |
32 | import org.kuali.rice.kew.dto.ModifiableDocumentContentDTO; | |
33 | import org.kuali.rice.kew.dto.MovePointDTO; | |
34 | import org.kuali.rice.kew.dto.NoteDTO; | |
35 | import org.kuali.rice.kew.dto.ReturnPointDTO; | |
36 | import org.kuali.rice.kew.dto.RouteHeaderDTO; | |
37 | import org.kuali.rice.kew.dto.RouteNodeInstanceDTO; | |
38 | import org.kuali.rice.kew.dto.UserIdDTO; | |
39 | import org.kuali.rice.kew.dto.WorkflowAttributeDefinitionDTO; | |
40 | import org.kuali.rice.kew.dto.WorkflowAttributeValidationErrorDTO; | |
41 | import org.kuali.rice.kew.exception.WorkflowException; | |
42 | import org.kuali.rice.kew.util.KEWConstants; | |
43 | ||
44 | /** | |
45 | * Represents a document in Workflow from the perspective of the client. This class is one of two | |
46 | * (Java) client interfaces to the KEW system (the other being {@link WorkflowInfo} class). The | |
47 | * first time an instance of this class is created, it will read the client configuration to determine | |
48 | * how to connect to KEW. | |
49 | * | |
50 | * <p>This class is used by creating new instances using the appropriate constructor. To create a new | |
51 | * document in KEW, create an instance of this class passing a UserIdVO and a | |
52 | * document type name. To load an existing document, create an instance of this class passing a | |
53 | * UserIdVO and a document ID number. | |
54 | * | |
55 | * <p>Internally, this wrapper interacts with the {@link WorkflowDocumentActions} service exported | |
56 | * over the KSB, maintaining state. | |
57 | * | |
58 | * <p>This class is not thread safe and must by synchronized externally for concurrent access. | |
59 | * | |
60 | * @author Kuali Rice Team (rice.collab@kuali.org) | |
61 | */ | |
62 | public class WorkflowDocument implements java.io.Serializable { | |
63 | ||
64 | private static final long serialVersionUID = -3672966990721719088L; | |
65 | ||
66 | /** | |
67 | * The principal ID of the user as whom actions will be taken on the KEW document | |
68 | */ | |
69 | private String principalId; | |
70 | /** | |
71 | * RouteHeader VO of the KEW document this WorkflowDocument represents | |
72 | */ | |
73 | private RouteHeaderDTO routeHeader; | |
74 | /** | |
75 | * Flag that indicates whether the document content currently loaded needs to be refreshed. | |
76 | * This is the case either if the document content has not yet been loaded, or an action | |
77 | * that might possibly affect the document content (which is potentially any action) has | |
78 | * subsequently been taken on the document through this API. | |
79 | */ | |
80 | 0 | private boolean documentContentDirty = false; |
81 | /** | |
82 | * Value Object encapsulating the document content | |
83 | */ | |
84 | private ModifiableDocumentContentDTO documentContent; | |
85 | ||
86 | /** | |
87 | * This method constructs a WorkflowDocument representing a new document in the | |
88 | * workflow system. Creation/committing of the new document is deferred until | |
89 | * the first action is taken on the document. | |
90 | * | |
91 | * @param principalId the user as which to take actions on the document | |
92 | * @param documentType the type of the document to create | |
93 | * @throws WorkflowException if anything goes awry | |
94 | */ | |
95 | public static WorkflowDocument createDocument(String principalId, String documentType) throws WorkflowException { | |
96 | 0 | return new WorkflowDocument(principalId, documentType, null); |
97 | } | |
98 | ||
99 | /** | |
100 | * This method loads a workflow document with the given document ID for the | |
101 | * given principalId. If no document can be found with the | |
102 | * given ID, then the {@link getRouteHeader()} method of the WorkflowDocument | |
103 | * which is created will return null. | |
104 | * | |
105 | * @param principalId the user as which to take actions on the document | |
106 | * @param documentId the document id of the document to load | |
107 | * @throws WorkflowException if anything goes awry | |
108 | */ | |
109 | public static WorkflowDocument loadDocument(String principalId, String documentId) throws WorkflowException { | |
110 | 0 | return new WorkflowDocument(principalId, null, documentId); |
111 | } | |
112 | ||
113 | 0 | protected WorkflowDocument(String principalId, String documentType, String documentId) throws WorkflowException { |
114 | 0 | init(principalId, documentType, documentId); |
115 | 0 | } |
116 | ||
117 | /** | |
118 | * Initializes this WorkflowDocument object, by either attempting to load an existing document by documentId | |
119 | * if one is supplied (non-null), or by constructing an empty document of the specified type. | |
120 | * @param principalId the user under which actions via this API on the specified document will be taken | |
121 | * @param documentType the type of document this WorkflowDocument should represent (either this parameter or documentId must be specified, non-null) | |
122 | * @param documentId the id of an existing document to load (either this parameter or documentType must be specified, non-null) | |
123 | * @throws WorkflowException if a documentId is specified but an exception occurs trying to load the document route header | |
124 | */ | |
125 | private void init(String principalId, String documentType, String documentId) throws WorkflowException { | |
126 | 0 | this.principalId = principalId; |
127 | 0 | routeHeader = new RouteHeaderDTO(); |
128 | 0 | routeHeader.setDocTypeName(documentType); |
129 | 0 | if (documentId != null) { |
130 | 0 | routeHeader = getWorkflowUtility().getRouteHeaderWithPrincipal(principalId, documentId); |
131 | } | |
132 | 0 | } |
133 | ||
134 | /** | |
135 | * Retrieves the WorkflowUtility proxy from the locator. The locator will cache this for us. | |
136 | */ | |
137 | private WorkflowUtility getWorkflowUtility() throws WorkflowException { | |
138 | 0 | WorkflowUtility workflowUtility = |
139 | (WorkflowUtility)GlobalResourceLoader.getService(KEWConstants.WORKFLOW_UTILITY_SERVICE); | |
140 | 0 | if (workflowUtility == null) { |
141 | 0 | throw new WorkflowException("Could not locate the WorkflowUtility service. Please ensure that KEW client is configured properly!"); |
142 | } | |
143 | 0 | return workflowUtility; |
144 | ||
145 | } | |
146 | ||
147 | /** | |
148 | * Retrieves the WorkflowDocumentActions proxy from the locator. The locator will cache this for us. | |
149 | */ | |
150 | private WorkflowDocumentActions getWorkflowDocumentActions() throws WorkflowException { | |
151 | 0 | WorkflowDocumentActions workflowDocumentActions = |
152 | (WorkflowDocumentActions) GlobalResourceLoader.getService(KEWConstants.WORKFLOW_DOCUMENT_ACTIONS_SERVICE); | |
153 | 0 | if (workflowDocumentActions == null) { |
154 | 0 | throw new WorkflowException("Could not locate the WorkflowDocumentActions service. Please ensure that KEW client is configured properly!"); |
155 | } | |
156 | 0 | return workflowDocumentActions; |
157 | } | |
158 | ||
159 | // ######################## | |
160 | // Document Content methods | |
161 | // ######################## | |
162 | ||
163 | /** | |
164 | * Returns an up-to-date DocumentContent of this document. | |
165 | * @see WorkflowUtility#getDocumentContent(Long) | |
166 | */ | |
167 | public DocumentContentDTO getDocumentContent() { | |
168 | try { | |
169 | // create the document if it hasn't already been created | |
170 | 0 | if (getRouteHeader().getDocumentId() == null) { |
171 | 0 | routeHeader = getWorkflowDocumentActions().createDocument(principalId, getRouteHeader()); |
172 | } | |
173 | 0 | if (documentContent == null || documentContentDirty) { |
174 | 0 | documentContent = new ModifiableDocumentContentDTO(getWorkflowUtility().getDocumentContent(routeHeader.getDocumentId())); |
175 | 0 | documentContentDirty = false; |
176 | } | |
177 | 0 | } catch (Exception e) { |
178 | 0 | throw handleExceptionAsRuntime(e); |
179 | 0 | } |
180 | 0 | return documentContent; |
181 | } | |
182 | ||
183 | /** | |
184 | * Returns the application specific section of the document content. This is | |
185 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
186 | * | |
187 | * For documents routed prior to Workflow 2.0: | |
188 | * If the application did NOT use attributes for XML generation, this method will | |
189 | * return the entire document content XML. Otherwise it will return the empty string. | |
190 | * @see DocumentContentDTO#getApplicationContent() | |
191 | */ | |
192 | public String getApplicationContent() { | |
193 | 0 | return getDocumentContent().getApplicationContent(); |
194 | } | |
195 | ||
196 | /** | |
197 | * Sets the application specific section of the document content. This is | |
198 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
199 | */ | |
200 | public void setApplicationContent(String applicationContent) { | |
201 | 0 | getDocumentContent().setApplicationContent(applicationContent); |
202 | 0 | } |
203 | ||
204 | /** | |
205 | * Clears all attribute document content from the document. | |
206 | * Typically, this will be used if it is necessary to update the attribute doc content on | |
207 | * the document. This can be accomplished by clearing the content and then adding the | |
208 | * desired attribute definitions. | |
209 | * | |
210 | * This is a convenience method that delegates to the {@link DocumentContentDTO}. | |
211 | * | |
212 | * In order for these changes to take effect, an action must be performed on the document (such as "save"). | |
213 | */ | |
214 | public void clearAttributeContent() { | |
215 | 0 | getDocumentContent().setAttributeContent(""); |
216 | 0 | } |
217 | ||
218 | /** | |
219 | * Returns the attribute-generated section of the document content. This is | |
220 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
221 | * @see DocumentContentDTO#getAttributeContent() | |
222 | */ | |
223 | public String getAttributeContent() { | |
224 | 0 | return getDocumentContent().getAttributeContent(); |
225 | } | |
226 | ||
227 | /** | |
228 | * Adds an attribute definition which defines creation parameters for a WorkflowAttribute | |
229 | * implementation. The created attribute will be used to generate attribute document content. | |
230 | * When the document is sent to the server, this will be appended to the existing attribute | |
231 | * doc content. If it is required to replace the attribute document content, then the | |
232 | * clearAttributeContent() method should be invoked prior to adding attribute definitions. | |
233 | * | |
234 | * This is a convenience method that delegates to the {@link DocumentContentDTO}. | |
235 | * @see DocumentContentDTO#addAttributeDefinition(WorkflowAttributeDefinitionDTO) | |
236 | */ | |
237 | public void addAttributeDefinition(WorkflowAttributeDefinitionDTO attributeDefinition) { | |
238 | 0 | getDocumentContent().addAttributeDefinition(attributeDefinition); |
239 | 0 | } |
240 | ||
241 | /** | |
242 | * Validate the WorkflowAttributeDefinition against it's attribute on the server. This will validate | |
243 | * the inputs that will eventually become xml. | |
244 | * | |
245 | * Only applies to attributes implementing WorkflowAttributeXmlValidator. | |
246 | * | |
247 | * This is a call through to the WorkflowInfo object and is here for convenience. | |
248 | * | |
249 | * @param attributeDefinition the workflow attribute definition VO to validate | |
250 | * @return WorkflowAttributeValidationErrorVO[] of error from the attribute | |
251 | * @throws WorkflowException when attribute doesn't implement WorkflowAttributeXmlValidator | |
252 | * @see WorkflowUtility#validateWorkflowAttributeDefinitionVO(WorkflowAttributeDefinitionDTO) | |
253 | */ | |
254 | public WorkflowAttributeValidationErrorDTO[] validateAttributeDefinition(WorkflowAttributeDefinitionDTO attributeDefinition) throws WorkflowException { | |
255 | 0 | return getWorkflowUtility().validateWorkflowAttributeDefinitionVO(attributeDefinition); |
256 | } | |
257 | ||
258 | /** | |
259 | * Removes an attribute definition from the document content. This is | |
260 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
261 | * @param attributeDefinition the attribute definition VO to remove | |
262 | */ | |
263 | public void removeAttributeDefinition(WorkflowAttributeDefinitionDTO attributeDefinition) { | |
264 | 0 | getDocumentContent().removeAttributeDefinition(attributeDefinition); |
265 | 0 | } |
266 | ||
267 | /** | |
268 | * Removes all attribute definitions from the document content. This is | |
269 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
270 | */ | |
271 | public void clearAttributeDefinitions() { | |
272 | 0 | getDocumentContent().setAttributeDefinitions(new WorkflowAttributeDefinitionDTO[0]); |
273 | 0 | } |
274 | ||
275 | /** | |
276 | * Returns the attribute definition VOs currently defined on the document content. This is | |
277 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
278 | * @return the attribute definition VOs currently defined on the document content. | |
279 | * @see DocumentContentDTO#getAttributeDefinitions() | |
280 | */ | |
281 | public WorkflowAttributeDefinitionDTO[] getAttributeDefinitions() { | |
282 | 0 | return getDocumentContent().getAttributeDefinitions(); |
283 | } | |
284 | ||
285 | /** | |
286 | * Adds a searchable attribute definition which defines creation parameters for a SearchableAttribute | |
287 | * implementation. The created attribute will be used to generate searchable document content. | |
288 | * When the document is sent to the server, this will be appended to the existing searchable | |
289 | * doc content. If it is required to replace the searchable document content, then the | |
290 | * clearSearchableContent() method should be invoked prior to adding definitions. This is | |
291 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
292 | */ | |
293 | public void addSearchableDefinition(WorkflowAttributeDefinitionDTO searchableDefinition) { | |
294 | 0 | getDocumentContent().addSearchableDefinition(searchableDefinition); |
295 | 0 | } |
296 | ||
297 | /** | |
298 | * Removes a searchable attribute definition from the document content. This is | |
299 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
300 | * @param searchableDefinition the searchable attribute definition to remove | |
301 | */ | |
302 | public void removeSearchableDefinition(WorkflowAttributeDefinitionDTO searchableDefinition) { | |
303 | 0 | getDocumentContent().removeSearchableDefinition(searchableDefinition); |
304 | 0 | } |
305 | ||
306 | /** | |
307 | * Removes all searchable attribute definitions from the document content. This is | |
308 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
309 | */ | |
310 | public void clearSearchableDefinitions() { | |
311 | 0 | getDocumentContent().setSearchableDefinitions(new WorkflowAttributeDefinitionDTO[0]); |
312 | 0 | } |
313 | ||
314 | /** | |
315 | * Clears the searchable content from the document content. This is | |
316 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
317 | */ | |
318 | public void clearSearchableContent() { | |
319 | 0 | getDocumentContent().setSearchableContent(""); |
320 | 0 | } |
321 | ||
322 | /** | |
323 | * Returns the searchable attribute definitions on the document content. This is | |
324 | * a convenience method that delegates to the {@link DocumentContentDTO}. | |
325 | * @return the searchable attribute definitions on the document content. | |
326 | * @see DocumentContentDTO#getSearchableDefinitions() | |
327 | */ | |
328 | public WorkflowAttributeDefinitionDTO[] getSearchableDefinitions() { | |
329 | 0 | return getDocumentContent().getSearchableDefinitions(); |
330 | } | |
331 | ||
332 | // ######################## | |
333 | // END Document Content methods | |
334 | // ######################## | |
335 | ||
336 | /** | |
337 | * Returns the RouteHeaderVO for the workflow document this WorkflowDocument represents | |
338 | */ | |
339 | public RouteHeaderDTO getRouteHeader() { | |
340 | 0 | return routeHeader; |
341 | } | |
342 | ||
343 | /** | |
344 | * Returns the id of the workflow document this WorkflowDocument represents. If this is a new document | |
345 | * that has not yet been created, the document is first created (and therefore this will return a new id) | |
346 | * @return the id of the workflow document this WorkflowDocument represents | |
347 | * @throws WorkflowException if an error occurs during document creation | |
348 | */ | |
349 | public String getDocumentId() throws WorkflowException { | |
350 | 0 | createDocumentIfNeccessary(); |
351 | 0 | return getRouteHeader().getDocumentId(); |
352 | } | |
353 | ||
354 | /** | |
355 | * Returns VOs of the pending ActionRequests on this document. If this object represents a new document | |
356 | * that has not yet been created, then an empty array will be returned. The ordering of ActionRequests | |
357 | * returned by this method is not guaranteed. | |
358 | * | |
359 | * This method relies on the WorkflowUtility service | |
360 | * | |
361 | * @return VOs of the pending ActionRequests on this document | |
362 | * @throws WorkflowException if an error occurs obtaining the pending action requests for this document | |
363 | * @see WorkflowUtility#getActionRequests(Long) | |
364 | */ | |
365 | public ActionRequestDTO[] getActionRequests() throws WorkflowException { | |
366 | 0 | if (getDocumentId() == null) { |
367 | 0 | return new ActionRequestDTO[0]; |
368 | } | |
369 | 0 | return getWorkflowUtility().getAllActionRequests(getDocumentId()); |
370 | } | |
371 | ||
372 | /** | |
373 | * Returns VOs of the actions taken on this document. If this object represents a new document | |
374 | * that has not yet been created, then an empty array will be returned. The ordering of actions taken | |
375 | * returned by this method is not guaranteed. | |
376 | * | |
377 | * This method relies on the WorkflowUtility service | |
378 | * | |
379 | * @return VOs of the actions that have been taken on this document | |
380 | * @throws WorkflowException if an error occurs obtaining the actions taken on this document | |
381 | * @see WorkflowUtility#getActionsTaken(Long) | |
382 | */ | |
383 | public ActionTakenDTO[] getActionsTaken() throws WorkflowException { | |
384 | 0 | if (getDocumentId() == null) { |
385 | 0 | return new ActionTakenDTO[0]; |
386 | } | |
387 | 0 | return getWorkflowUtility().getActionsTaken(getDocumentId()); |
388 | } | |
389 | ||
390 | /** | |
391 | * Sets the "application doc id" on the document | |
392 | * @param appDocId the "application doc id" to set on the workflow document | |
393 | */ | |
394 | public void setAppDocId(String appDocId) { | |
395 | 0 | routeHeader.setAppDocId(appDocId); |
396 | 0 | } |
397 | ||
398 | /** | |
399 | * Returns the "application doc id" set on this workflow document (if any) | |
400 | * @return the "application doc id" set on this workflow document (if any) | |
401 | */ | |
402 | public String getAppDocId() { | |
403 | 0 | return routeHeader.getAppDocId(); |
404 | } | |
405 | ||
406 | /** | |
407 | * Returns the date/time the document was created, or null if the document has not yet been created | |
408 | * @return the date/time the document was created, or null if the document has not yet been created | |
409 | */ | |
410 | public Timestamp getDateCreated() { | |
411 | 0 | if (routeHeader.getDateCreated() == null) { |
412 | 0 | return null; |
413 | } | |
414 | 0 | return new Timestamp(routeHeader.getDateCreated().getTime().getTime()); |
415 | } | |
416 | ||
417 | /** | |
418 | * Returns the title of the document | |
419 | * @return the title of the document | |
420 | */ | |
421 | public String getTitle() { | |
422 | 0 | return getRouteHeader().getDocTitle(); |
423 | } | |
424 | ||
425 | /** | |
426 | * Performs the 'save' action on the document this WorkflowDocument represents. If this is a new document, | |
427 | * the document is created first. | |
428 | * @param annotation the message to log for the action | |
429 | * @throws WorkflowException in case an error occurs saving the document | |
430 | * @see WorkflowDocumentActions#saveDocument(UserIdDTO, RouteHeaderDTO, String) | |
431 | */ | |
432 | public void saveDocument(String annotation) throws WorkflowException { | |
433 | 0 | createDocumentIfNeccessary(); |
434 | 0 | routeHeader = getWorkflowDocumentActions().saveDocument(principalId, getRouteHeader(), annotation); |
435 | 0 | documentContentDirty = true; |
436 | 0 | } |
437 | ||
438 | /** | |
439 | * Performs the 'route' action on the document this WorkflowDocument represents. If this is a new document, | |
440 | * the document is created first. | |
441 | * @param annotation the message to log for the action | |
442 | * @throws WorkflowException in case an error occurs routing the document | |
443 | * @see WorkflowDocumentActions#routeDocument(UserIdDTO, RouteHeaderDTO, String) | |
444 | */ | |
445 | public void routeDocument(String annotation) throws WorkflowException { | |
446 | 0 | createDocumentIfNeccessary(); |
447 | 0 | routeHeader = getWorkflowDocumentActions().routeDocument(principalId, routeHeader, annotation); |
448 | 0 | documentContentDirty = true; |
449 | 0 | } |
450 | ||
451 | /** | |
452 | * Performs the 'disapprove' action on the document this WorkflowDocument represents. If this is a new document, | |
453 | * the document is created first. | |
454 | * @param annotation the message to log for the action | |
455 | * @throws WorkflowException in case an error occurs disapproving the document | |
456 | * @see WorkflowDocumentActions#disapproveDocument(UserIdDTO, RouteHeaderDTO, String) | |
457 | */ | |
458 | public void disapprove(String annotation) throws WorkflowException { | |
459 | 0 | createDocumentIfNeccessary(); |
460 | 0 | routeHeader = getWorkflowDocumentActions().disapproveDocument(principalId, getRouteHeader(), annotation); |
461 | 0 | documentContentDirty = true; |
462 | 0 | } |
463 | ||
464 | /** | |
465 | * Performs the 'approve' action on the document this WorkflowDocument represents. If this is a new document, | |
466 | * the document is created first. | |
467 | * @param annotation the message to log for the action | |
468 | * @throws WorkflowException in case an error occurs approving the document | |
469 | * @see WorkflowDocumentActions#approveDocument(UserIdDTO, RouteHeaderDTO, String) | |
470 | */ | |
471 | public void approve(String annotation) throws WorkflowException { | |
472 | 0 | createDocumentIfNeccessary(); |
473 | 0 | routeHeader = getWorkflowDocumentActions().approveDocument(principalId, getRouteHeader(), annotation); |
474 | 0 | documentContentDirty = true; |
475 | 0 | } |
476 | ||
477 | /** | |
478 | * Performs the 'cancel' action on the document this WorkflowDocument represents. If this is a new document, | |
479 | * the document is created first. | |
480 | * @param annotation the message to log for the action | |
481 | * @throws WorkflowException in case an error occurs canceling the document | |
482 | * @see WorkflowDocumentActions#cancelDocument(UserIdDTO, RouteHeaderDTO, String) | |
483 | */ | |
484 | public void cancel(String annotation) throws WorkflowException { | |
485 | 0 | createDocumentIfNeccessary(); |
486 | 0 | routeHeader = getWorkflowDocumentActions().cancelDocument(principalId, getRouteHeader(), annotation); |
487 | 0 | documentContentDirty = true; |
488 | 0 | } |
489 | ||
490 | /** | |
491 | * Performs the 'blanket-approve' action on the document this WorkflowDocument represents. If this is a new document, | |
492 | * the document is created first. | |
493 | * @param annotation the message to log for the action | |
494 | * @throws WorkflowException in case an error occurs blanket-approving the document | |
495 | * @see WorkflowDocumentActions#blanketApprovalToNodes(UserIdDTO, RouteHeaderDTO, String, String[]) | |
496 | */ | |
497 | public void blanketApprove(String annotation) throws WorkflowException { | |
498 | 0 | blanketApprove(annotation, (String)null); |
499 | 0 | } |
500 | ||
501 | /** | |
502 | * Commits any changes made to the local copy of this document to the workflow system. If this is a new document, | |
503 | * the document is created first. | |
504 | * @throws WorkflowException in case an error occurs saving the document | |
505 | * @see WorkflowDocumentActions#saveRoutingData(UserIdDTO, RouteHeaderDTO) | |
506 | */ | |
507 | public void saveRoutingData() throws WorkflowException { | |
508 | 0 | createDocumentIfNeccessary(); |
509 | 0 | routeHeader = getWorkflowDocumentActions().saveRoutingData(principalId, getRouteHeader()); |
510 | 0 | documentContentDirty = true; |
511 | 0 | } |
512 | ||
513 | /** | |
514 | * | |
515 | * This method sets the Application Document Status and then calls saveRoutingData() to commit | |
516 | * the changes to the workflow system. | |
517 | * | |
518 | * @param appDocStatus | |
519 | * @throws WorkflowException | |
520 | */ | |
521 | public void updateAppDocStatus(String appDocStatus) throws WorkflowException { | |
522 | 0 | getRouteHeader().setAppDocStatus(appDocStatus); |
523 | 0 | saveRoutingData(); |
524 | 0 | } |
525 | ||
526 | /** | |
527 | * Performs the 'acknowledge' action on the document this WorkflowDocument represents. If this is a new document, | |
528 | * the document is created first. | |
529 | * @param annotation the message to log for the action | |
530 | * @throws WorkflowException in case an error occurs acknowledging the document | |
531 | * @see WorkflowDocumentActions#acknowledgeDocument(UserIdDTO, RouteHeaderDTO, String) | |
532 | */ | |
533 | public void acknowledge(String annotation) throws WorkflowException { | |
534 | 0 | createDocumentIfNeccessary(); |
535 | 0 | routeHeader = getWorkflowDocumentActions().acknowledgeDocument(principalId, getRouteHeader(), annotation); |
536 | 0 | documentContentDirty = true; |
537 | 0 | } |
538 | ||
539 | /** | |
540 | * Performs the 'fyi' action on the document this WorkflowDocument represents. If this is a new document, | |
541 | * the document is created first. | |
542 | * @param annotation the message to log for the action | |
543 | * @throws WorkflowException in case an error occurs fyi-ing the document | |
544 | */ | |
545 | public void fyi() throws WorkflowException { | |
546 | 0 | createDocumentIfNeccessary(); |
547 | 0 | routeHeader = getWorkflowDocumentActions().clearFYIDocument(principalId, getRouteHeader()); |
548 | 0 | documentContentDirty = true; |
549 | 0 | } |
550 | ||
551 | /** | |
552 | * Performs the 'delete' action on the document this WorkflowDocument represents. If this is a new document, | |
553 | * the document is created first. | |
554 | * @param annotation the message to log for the action | |
555 | * @throws WorkflowException in case an error occurs deleting the document | |
556 | * @see WorkflowDocumentActions#deleteDocument(UserIdDTO, RouteHeaderDTO) | |
557 | */ | |
558 | public void delete() throws WorkflowException { | |
559 | 0 | createDocumentIfNeccessary(); |
560 | 0 | getWorkflowDocumentActions().deleteDocument(principalId, getRouteHeader()); |
561 | 0 | documentContentDirty = true; |
562 | 0 | } |
563 | ||
564 | /** | |
565 | * Reloads the document route header. If this is a new document, the document is created first. | |
566 | * Next time document content is accessed, an up-to-date copy will be retrieved from workflow. | |
567 | * @throws WorkflowException in case an error occurs retrieving the route header | |
568 | */ | |
569 | public void refreshContent() throws WorkflowException { | |
570 | 0 | createDocumentIfNeccessary(); |
571 | 0 | routeHeader = getWorkflowUtility().getRouteHeader(getDocumentId()); |
572 | 0 | documentContentDirty = true; |
573 | 0 | } |
574 | ||
575 | /** | |
576 | * Sends an ad hoc request to the specified user at the current active node on the document. If the document is | |
577 | * in a terminal state, the request will be attached to the terminal node. | |
578 | */ | |
579 | public void adHocRouteDocumentToPrincipal(String actionRequested, String annotation, String principalId, String responsibilityDesc, boolean forceAction) throws WorkflowException { | |
580 | 0 | adHocRouteDocumentToPrincipal(actionRequested, null, annotation, principalId, responsibilityDesc, forceAction); |
581 | 0 | } |
582 | ||
583 | /** | |
584 | * Sends an ad hoc request to the specified user at the specified node on the document. If the document is | |
585 | * in a terminal state, the request will be attached to the terminal node. | |
586 | */ | |
587 | public void adHocRouteDocumentToPrincipal(String actionRequested, String nodeName, String annotation, String principalId, String responsibilityDesc, boolean forceAction) throws WorkflowException { | |
588 | 0 | adHocRouteDocumentToPrincipal(actionRequested, nodeName, annotation, principalId, responsibilityDesc, forceAction, null); |
589 | 0 | } |
590 | ||
591 | /** | |
592 | * Sends an ad hoc request to the specified user at the specified node on the document. If the document is | |
593 | * in a terminal state, the request will be attached to the terminal node. | |
594 | */ | |
595 | public void adHocRouteDocumentToPrincipal(String actionRequested, String nodeName, String annotation, String principalId, String responsibilityDesc, boolean forceAction, String requestLabel) throws WorkflowException { | |
596 | 0 | createDocumentIfNeccessary(); |
597 | 0 | routeHeader = getWorkflowDocumentActions().adHocRouteDocumentToPrincipal(principalId, getRouteHeader(), actionRequested, nodeName, annotation, principalId, responsibilityDesc, forceAction, requestLabel); |
598 | 0 | documentContentDirty = true; |
599 | 0 | } |
600 | ||
601 | /** | |
602 | * Sends an ad hoc request to the specified workgroup at the current active node on the document. If the document is | |
603 | * in a terminal state, the request will be attached to the terminal node. | |
604 | */ | |
605 | public void adHocRouteDocumentToGroup(String actionRequested, String annotation, String groupId, String responsibilityDesc, boolean forceAction) throws WorkflowException { | |
606 | 0 | adHocRouteDocumentToGroup(actionRequested, null, annotation, groupId, responsibilityDesc, forceAction); |
607 | 0 | } |
608 | ||
609 | /** | |
610 | * Sends an ad hoc request to the specified workgroup at the specified node on the document. If the document is | |
611 | * in a terminal state, the request will be attached to the terminal node. | |
612 | */ | |
613 | public void adHocRouteDocumentToGroup(String actionRequested, String nodeName, String annotation, String groupId, String responsibilityDesc, boolean forceAction) throws WorkflowException { | |
614 | 0 | adHocRouteDocumentToGroup(actionRequested, nodeName, annotation, groupId, responsibilityDesc, forceAction, null); |
615 | 0 | } |
616 | ||
617 | /** | |
618 | * Sends an ad hoc request to the specified workgroup at the specified node on the document. If the document is | |
619 | * in a terminal state, the request will be attached to the terminal node. | |
620 | */ | |
621 | public void adHocRouteDocumentToGroup(String actionRequested, String nodeName, String annotation, String groupId, String responsibilityDesc, boolean forceAction, String requestLabel) throws WorkflowException { | |
622 | 0 | createDocumentIfNeccessary(); |
623 | 0 | routeHeader = getWorkflowDocumentActions().adHocRouteDocumentToGroup(principalId, getRouteHeader(), actionRequested, nodeName, annotation, groupId, responsibilityDesc, forceAction, requestLabel); |
624 | 0 | documentContentDirty = true; |
625 | 0 | } |
626 | ||
627 | /** | |
628 | * Revokes AdHoc request(s) according to the given AdHocRevokeVO which is passed in. | |
629 | * | |
630 | * 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 | |
631 | * WorkflowException. | |
632 | * @param revoke AdHocRevokeVO | |
633 | * @param annotation message to note for this action | |
634 | * @throws WorkflowException if an error occurs revoking adhoc requests | |
635 | * @see WorkflowDocumentActions#revokeAdHocRequests(UserIdDTO, RouteHeaderDTO, AdHocRevokeDTO, String) | |
636 | */ | |
637 | public void revokeAdHocRequests(AdHocRevokeDTO revoke, String annotation) throws WorkflowException { | |
638 | 0 | if (getRouteHeader().getDocumentId() == null) { |
639 | 0 | throw new WorkflowException("Can't revoke request, the workflow document has not yet been created!"); |
640 | } | |
641 | 0 | createDocumentIfNeccessary(); |
642 | 0 | routeHeader = getWorkflowDocumentActions().revokeAdHocRequests(principalId, getRouteHeader(), revoke, annotation); |
643 | 0 | documentContentDirty = true; |
644 | 0 | } |
645 | ||
646 | /** | |
647 | * Sets the title of the document, empty string if null is specified. | |
648 | * @param title title of the document to set, or null | |
649 | */ | |
650 | // WorkflowException is declared but not thrown... | |
651 | public void setTitle(String title) throws WorkflowException { | |
652 | 0 | if (title == null) { |
653 | 0 | title = ""; |
654 | } | |
655 | 0 | if (title.length() > KEWConstants.TITLE_MAX_LENGTH) { |
656 | 0 | title = title.substring(0, KEWConstants.TITLE_MAX_LENGTH); |
657 | } | |
658 | 0 | getRouteHeader().setDocTitle(title); |
659 | 0 | } |
660 | ||
661 | /** | |
662 | * Returns the document type of the workflow document | |
663 | * @return the document type of the workflow document | |
664 | * @throws RuntimeException if document does not exist (is not yet created) | |
665 | * @see RouteHeaderDTO#getDocTypeName() | |
666 | */ | |
667 | public String getDocumentType() { | |
668 | 0 | if (getRouteHeader() == null) { |
669 | // HACK: FIXME: we should probably proscribe, or at least handle consistently, these corner cases | |
670 | // NPEs are not nice | |
671 | 0 | throw new RuntimeException("No such document!"); |
672 | } | |
673 | 0 | return getRouteHeader().getDocTypeName(); |
674 | } | |
675 | ||
676 | /** | |
677 | * Returns whether an acknowledge is requested of the user for this document. This is | |
678 | * a convenience method that delegates to {@link RouteHeaderDTO#isAckRequested()}. | |
679 | * @return whether an acknowledge is requested of the user for this document | |
680 | * @see RouteHeaderDTO#isAckRequested() | |
681 | */ | |
682 | public boolean isAcknowledgeRequested() { | |
683 | 0 | return getRouteHeader().isAckRequested(); |
684 | } | |
685 | ||
686 | /** | |
687 | * Returns whether an approval is requested of the user for this document. This is | |
688 | * a convenience method that delegates to {@link RouteHeaderDTO#isApproveRequested()}. | |
689 | * @return whether an approval is requested of the user for this document | |
690 | * @see RouteHeaderDTO#isApproveRequested() | |
691 | */ | |
692 | public boolean isApprovalRequested() { | |
693 | 0 | return getRouteHeader().isApproveRequested(); |
694 | } | |
695 | ||
696 | /** | |
697 | * Returns whether a completion is requested of the user for this document. This is | |
698 | * a convenience method that delegates to {@link RouteHeaderDTO#isCompleteRequested()}. | |
699 | * @return whether an approval is requested of the user for this document | |
700 | * @see RouteHeaderDTO#isCompleteRequested() | |
701 | */ | |
702 | public boolean isCompletionRequested() { | |
703 | 0 | return getRouteHeader().isCompleteRequested(); |
704 | } | |
705 | ||
706 | /** | |
707 | * Returns whether an FYI is requested of the user for this document. This is | |
708 | * a convenience method that delegates to {@link RouteHeaderDTO#isFyiRequested()}. | |
709 | * @return whether an FYI is requested of the user for this document | |
710 | * @see RouteHeaderDTO#isFyiRequested() | |
711 | */ | |
712 | public boolean isFYIRequested() { | |
713 | 0 | return getRouteHeader().isFyiRequested(); |
714 | } | |
715 | ||
716 | /** | |
717 | * Returns whether the user can blanket approve the document | |
718 | * @return whether the user can blanket approve the document | |
719 | * @see RouteHeaderDTO#getValidActions() | |
720 | */ | |
721 | public boolean isBlanketApproveCapable() { | |
722 | // TODO delyea - refactor this to take into account non-initiator owned documents | |
723 | 0 | return getRouteHeader().getValidActions().contains(KEWConstants.ACTION_TAKEN_BLANKET_APPROVE_CD) && (isCompletionRequested() || isApprovalRequested() || stateIsInitiated()); |
724 | } | |
725 | ||
726 | /** | |
727 | * Returns whether the specified action code is valid for the current user and document | |
728 | * @return whether the user can blanket approve the document | |
729 | * @see RouteHeaderDTO#getValidActions() | |
730 | */ | |
731 | public boolean isActionCodeValidForDocument(String actionTakenCode) { | |
732 | 0 | return getRouteHeader().getValidActions().contains(actionTakenCode); |
733 | } | |
734 | ||
735 | /** | |
736 | * Performs the 'super-user-approve' action on the document this WorkflowDocument represents. If this is a new document, | |
737 | * the document is created first. | |
738 | * @param annotation the message to log for the action | |
739 | * @throws WorkflowException in case an error occurs super-user-approve-ing the document | |
740 | * @see WorkflowDocumentActions#superUserApprove(UserIdDTO, RouteHeaderDTO, String) | |
741 | */ | |
742 | public void superUserApprove(String annotation) throws WorkflowException { | |
743 | 0 | createDocumentIfNeccessary(); |
744 | 0 | routeHeader = getWorkflowDocumentActions().superUserApprove(principalId, getRouteHeader(), annotation); |
745 | 0 | documentContentDirty = true; |
746 | 0 | } |
747 | ||
748 | /** | |
749 | * Performs the 'super-user-action-request-approve' action on the document this WorkflowDocument represents and the action | |
750 | * request the id represents. | |
751 | * @param actionRequestId the action request id for the action request the super user is approved | |
752 | * @param annotation the message to log for the action | |
753 | * @throws WorkflowException in case an error occurs super-user-action-request-approve-ing the document | |
754 | * @see WorkflowDocumentActions#superUserApprove(UserIdVO, RouteHeaderVO, String)(UserIdVO, RouteHeaderVO, String) | |
755 | */ | |
756 | public void superUserActionRequestApprove(Long actionRequestId, String annotation) throws WorkflowException { | |
757 | 0 | createDocumentIfNeccessary(); |
758 | 0 | routeHeader = getWorkflowDocumentActions().superUserActionRequestApprove(principalId, getRouteHeader(), actionRequestId, annotation); |
759 | 0 | documentContentDirty = true; |
760 | 0 | } |
761 | ||
762 | /** | |
763 | * Performs the 'super-user-disapprove' action on the document this WorkflowDocument represents. If this is a new document, | |
764 | * the document is created first. | |
765 | * @param annotation the message to log for the action | |
766 | * @throws WorkflowException in case an error occurs super-user-disapprove-ing the document | |
767 | * @see WorkflowDocumentActions#superUserDisapprove(UserIdDTO, RouteHeaderDTO, String) | |
768 | */ | |
769 | public void superUserDisapprove(String annotation) throws WorkflowException { | |
770 | 0 | createDocumentIfNeccessary(); |
771 | 0 | routeHeader = getWorkflowDocumentActions().superUserDisapprove(principalId, getRouteHeader(), annotation); |
772 | 0 | documentContentDirty = true; |
773 | 0 | } |
774 | ||
775 | /** | |
776 | * Performs the 'super-user-cancel' action on the document this WorkflowDocument represents. If this is a new document, | |
777 | * the document is created first. | |
778 | * @param annotation the message to log for the action | |
779 | * @throws WorkflowException in case an error occurs super-user-cancel-ing the document | |
780 | * @see WorkflowDocumentActions#superUserCancel(UserIdDTO, RouteHeaderDTO, String) | |
781 | */ | |
782 | public void superUserCancel(String annotation) throws WorkflowException { | |
783 | 0 | createDocumentIfNeccessary(); |
784 | 0 | routeHeader = getWorkflowDocumentActions().superUserCancel(principalId, getRouteHeader(), annotation); |
785 | 0 | documentContentDirty = true; |
786 | 0 | } |
787 | ||
788 | /** | |
789 | * Returns whether the user is a super user on this document | |
790 | * @return whether the user is a super user on this document | |
791 | * @throws WorkflowException if an error occurs determining whether the user is a super user on this document | |
792 | * @see WorkflowUtility#isSuperUserForDocumentType(UserIdDTO, Long) | |
793 | */ | |
794 | public boolean isSuperUser() throws WorkflowException { | |
795 | 0 | createDocumentIfNeccessary(); |
796 | 0 | return getWorkflowUtility().isSuperUserForDocumentType(principalId, getRouteHeader().getDocTypeId()); |
797 | } | |
798 | ||
799 | /** | |
800 | * Returns whether the user passed into WorkflowDocument at instantiation can route | |
801 | * the document. | |
802 | * @return if user passed into WorkflowDocument at instantiation can route | |
803 | * the document. | |
804 | */ | |
805 | public boolean isRouteCapable() { | |
806 | 0 | return isActionCodeValidForDocument(KEWConstants.ACTION_TAKEN_ROUTED_CD); |
807 | } | |
808 | ||
809 | /** | |
810 | * Performs the 'clearFYI' action on the document this WorkflowDocument represents. If this is a new document, | |
811 | * the document is created first. | |
812 | * @param annotation the message to log for the action | |
813 | * @throws WorkflowException in case an error occurs clearing FYI on the document | |
814 | * @see WorkflowDocumentActions#clearFYIDocument(UserIdDTO, RouteHeaderDTO) | |
815 | */ | |
816 | public void clearFYI() throws WorkflowException { | |
817 | 0 | createDocumentIfNeccessary(); |
818 | 0 | getWorkflowDocumentActions().clearFYIDocument(principalId, getRouteHeader()); |
819 | 0 | documentContentDirty = true; |
820 | 0 | } |
821 | ||
822 | /** | |
823 | * Performs the 'complete' action on the document this WorkflowDocument represents. If this is a new document, | |
824 | * the document is created first. | |
825 | * @param annotation the message to log for the action | |
826 | * @throws WorkflowException in case an error occurs clearing completing the document | |
827 | * @see WorkflowDocumentActions#completeDocument(UserIdDTO, RouteHeaderDTO, String) | |
828 | */ | |
829 | public void complete(String annotation) throws WorkflowException { | |
830 | 0 | createDocumentIfNeccessary(); |
831 | 0 | routeHeader = getWorkflowDocumentActions().completeDocument(principalId, getRouteHeader(), annotation); |
832 | 0 | documentContentDirty = true; |
833 | 0 | } |
834 | ||
835 | /** | |
836 | * Performs the 'logDocumentAction' action on the document this WorkflowDocument represents. If this is a new document, | |
837 | * the document is created first. The 'logDocumentAction' simply logs a message on the document. | |
838 | * @param annotation the message to log for the action | |
839 | * @throws WorkflowException in case an error occurs logging a document action on the document | |
840 | * @see WorkflowDocumentActions#logDocumentAction(UserIdDTO, RouteHeaderDTO, String) | |
841 | */ | |
842 | public void logDocumentAction(String annotation) throws WorkflowException { | |
843 | 0 | createDocumentIfNeccessary(); |
844 | 0 | getWorkflowDocumentActions().logDocumentAction(principalId, getRouteHeader(), annotation); |
845 | 0 | documentContentDirty = true; |
846 | 0 | } |
847 | ||
848 | /** | |
849 | * Indicates if the document is in the initiated state or not. | |
850 | * | |
851 | * @return true if in the specified state | |
852 | */ | |
853 | public boolean stateIsInitiated() { | |
854 | 0 | return KEWConstants.ROUTE_HEADER_INITIATED_CD.equals(getRouteHeader().getDocRouteStatus()); |
855 | } | |
856 | ||
857 | /** | |
858 | * Indicates if the document is in the saved state or not. | |
859 | * | |
860 | * @return true if in the specified state | |
861 | */ | |
862 | public boolean stateIsSaved() { | |
863 | 0 | return KEWConstants.ROUTE_HEADER_SAVED_CD.equals(getRouteHeader().getDocRouteStatus()); |
864 | } | |
865 | ||
866 | /** | |
867 | * Indicates if the document is in the enroute state or not. | |
868 | * | |
869 | * @return true if in the specified state | |
870 | */ | |
871 | public boolean stateIsEnroute() { | |
872 | 0 | return KEWConstants.ROUTE_HEADER_ENROUTE_CD.equals(getRouteHeader().getDocRouteStatus()); |
873 | } | |
874 | ||
875 | /** | |
876 | * Indicates if the document is in the exception state or not. | |
877 | * | |
878 | * @return true if in the specified state | |
879 | */ | |
880 | public boolean stateIsException() { | |
881 | 0 | return KEWConstants.ROUTE_HEADER_EXCEPTION_CD.equals(getRouteHeader().getDocRouteStatus()); |
882 | } | |
883 | ||
884 | /** | |
885 | * Indicates if the document is in the canceled state or not. | |
886 | * | |
887 | * @return true if in the specified state | |
888 | */ | |
889 | public boolean stateIsCanceled() { | |
890 | 0 | return KEWConstants.ROUTE_HEADER_CANCEL_CD.equals(getRouteHeader().getDocRouteStatus()); |
891 | } | |
892 | ||
893 | /** | |
894 | * Indicates if the document is in the disapproved state or not. | |
895 | * | |
896 | * @return true if in the specified state | |
897 | */ | |
898 | public boolean stateIsDisapproved() { | |
899 | 0 | return KEWConstants.ROUTE_HEADER_DISAPPROVED_CD.equals(getRouteHeader().getDocRouteStatus()); |
900 | } | |
901 | ||
902 | /** | |
903 | * Indicates if the document is in the approved state or not. Will answer true is document is in Processed or Finalized state. | |
904 | * | |
905 | * @return true if in the specified state | |
906 | */ | |
907 | public boolean stateIsApproved() { | |
908 | 0 | return KEWConstants.ROUTE_HEADER_APPROVED_CD.equals(getRouteHeader().getDocRouteStatus()) || stateIsProcessed() || stateIsFinal(); |
909 | } | |
910 | ||
911 | /** | |
912 | * Indicates if the document is in the processed state or not. | |
913 | * | |
914 | * @return true if in the specified state | |
915 | */ | |
916 | public boolean stateIsProcessed() { | |
917 | 0 | return KEWConstants.ROUTE_HEADER_PROCESSED_CD.equals(getRouteHeader().getDocRouteStatus()); |
918 | } | |
919 | ||
920 | /** | |
921 | * Indicates if the document is in the final state or not. | |
922 | * | |
923 | * @return true if in the specified state | |
924 | */ | |
925 | public boolean stateIsFinal() { | |
926 | 0 | return KEWConstants.ROUTE_HEADER_FINAL_CD.equals(getRouteHeader().getDocRouteStatus()); |
927 | } | |
928 | ||
929 | /** | |
930 | * Returns the display value of the current document status | |
931 | * @return the display value of the current document status | |
932 | */ | |
933 | public String getStatusDisplayValue() { | |
934 | 0 | return (String) KEWConstants.DOCUMENT_STATUSES.get(getRouteHeader().getDocRouteStatus()); |
935 | } | |
936 | ||
937 | /** | |
938 | * Returns the principalId with which this WorkflowDocument was constructed | |
939 | * @return the principalId with which this WorkflowDocument was constructed | |
940 | */ | |
941 | public String getPrincipalId() { | |
942 | 0 | return principalId; |
943 | } | |
944 | ||
945 | /** | |
946 | * Sets the principalId under which actions against this document should be taken | |
947 | * @param principalId principalId under which actions against this document should be taken | |
948 | */ | |
949 | public void setPrincipalId(String principalId) { | |
950 | 0 | this.principalId = principalId; |
951 | 0 | } |
952 | ||
953 | /** | |
954 | * Checks if the document has been created or not (i.e. has a document id or not) and issues | |
955 | * a call to the server to create the document if it has not yet been created. | |
956 | * | |
957 | * Also checks if the document content has been updated and saves it if it has. | |
958 | */ | |
959 | private void createDocumentIfNeccessary() throws WorkflowException { | |
960 | 0 | if (getRouteHeader().getDocumentId() == null) { |
961 | 0 | routeHeader = getWorkflowDocumentActions().createDocument(principalId, getRouteHeader()); |
962 | } | |
963 | 0 | if (documentContent != null && documentContent.isModified()) { |
964 | 0 | saveDocumentContent(documentContent); |
965 | } | |
966 | 0 | } |
967 | ||
968 | /** | |
969 | * Like handleException except it returns a RuntimeException. | |
970 | */ | |
971 | private RuntimeException handleExceptionAsRuntime(Exception e) { | |
972 | 0 | if (e instanceof RuntimeException) { |
973 | 0 | return (RuntimeException)e; |
974 | } | |
975 | 0 | return new WorkflowRuntimeException(e); |
976 | } | |
977 | ||
978 | // WORKFLOW 2.1: new methods | |
979 | ||
980 | /** | |
981 | * Performs the 'blanketApprove' action on the document this WorkflowDocument represents. If this is a new document, | |
982 | * the document is created first. | |
983 | * @param annotation the message to log for the action | |
984 | * @param nodeName the extent to which to blanket approve; blanket approval will stop at this node | |
985 | * @throws WorkflowException in case an error occurs blanket-approving the document | |
986 | * @see WorkflowDocumentActions#blanketApprovalToNodes(UserIdDTO, RouteHeaderDTO, String, String[]) | |
987 | */ | |
988 | public void blanketApprove(String annotation, String nodeName) throws WorkflowException { | |
989 | 0 | blanketApprove(annotation, (nodeName == null ? new String[] {} : new String[] { nodeName })); |
990 | 0 | } |
991 | ||
992 | /** | |
993 | * Performs the 'blanketApprove' action on the document this WorkflowDocument represents. If this is a new document, | |
994 | * the document is created first. | |
995 | * @param annotation the message to log for the action | |
996 | * @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) | |
997 | * @throws WorkflowException in case an error occurs blanket-approving the document | |
998 | * @see WorkflowDocumentActions#blanketApprovalToNodes(UserIdDTO, RouteHeaderDTO, String, String[]) | |
999 | */ | |
1000 | public void blanketApprove(String annotation, String[] nodeNames) throws WorkflowException { | |
1001 | 0 | createDocumentIfNeccessary(); |
1002 | 0 | routeHeader = getWorkflowDocumentActions().blanketApprovalToNodes(principalId, getRouteHeader(), annotation, nodeNames); |
1003 | 0 | documentContentDirty = true; |
1004 | 0 | } |
1005 | ||
1006 | /** | |
1007 | * The user taking action removes the action items for this workgroup and document from all other | |
1008 | * group members' action lists. If this is a new document, the document is created first. | |
1009 | * | |
1010 | * @param annotation the message to log for the action | |
1011 | * @param workgroupId the workgroup on which to take authority | |
1012 | * @throws WorkflowException user taking action is not in workgroup | |
1013 | */ | |
1014 | public void takeGroupAuthority(String annotation, String groupId) throws WorkflowException { | |
1015 | 0 | createDocumentIfNeccessary(); |
1016 | 0 | routeHeader = getWorkflowDocumentActions().takeGroupAuthority(principalId, getRouteHeader(), groupId, annotation); |
1017 | 0 | documentContentDirty = true; |
1018 | 0 | } |
1019 | ||
1020 | /** | |
1021 | * The user that took the group authority is putting the action items back in the other users action lists. | |
1022 | * If this is a new document, the document is created first. | |
1023 | * | |
1024 | * @param annotation the message to log for the action | |
1025 | * @param workgroupId the workgroup on which to take authority | |
1026 | * @throws WorkflowException user taking action is not in workgroup or did not take workgroup authority | |
1027 | */ | |
1028 | public void releaseGroupAuthority(String annotation, String groupId) throws WorkflowException { | |
1029 | 0 | createDocumentIfNeccessary(); |
1030 | 0 | routeHeader = getWorkflowDocumentActions().releaseGroupAuthority(principalId, getRouteHeader(), groupId, annotation); |
1031 | 0 | documentContentDirty = true; |
1032 | 0 | } |
1033 | ||
1034 | /** | |
1035 | * Returns names of all active nodes the document is currently at. | |
1036 | * | |
1037 | * @return names of all active nodes the document is currently at. | |
1038 | * @throws WorkflowException if there is an error obtaining the currently active nodes on the document | |
1039 | * @see WorkflowUtility#getActiveNodeInstances(Long) | |
1040 | */ | |
1041 | public String[] getNodeNames() throws WorkflowException { | |
1042 | 0 | RouteNodeInstanceDTO[] activeNodeInstances = getWorkflowUtility().getActiveNodeInstances(getDocumentId()); |
1043 | 0 | String[] nodeNames = new String[(activeNodeInstances == null ? 0 : activeNodeInstances.length)]; |
1044 | 0 | for (int index = 0; index < activeNodeInstances.length; index++) { |
1045 | 0 | nodeNames[index] = activeNodeInstances[index].getName(); |
1046 | } | |
1047 | 0 | return nodeNames; |
1048 | } | |
1049 | ||
1050 | /** | |
1051 | * Performs the 'returnToPrevious' action on the document this WorkflowDocument represents. If this is a new document, | |
1052 | * the document is created first. | |
1053 | * @param annotation the message to log for the action | |
1054 | * @param nodeName the node to return to | |
1055 | * @throws WorkflowException in case an error occurs returning to previous node | |
1056 | * @see WorkflowDocumentActions#returnDocumentToPreviousNode(UserIdDTO, RouteHeaderDTO, ReturnPointDTO, String) | |
1057 | */ | |
1058 | public void returnToPreviousNode(String annotation, String nodeName) throws WorkflowException { | |
1059 | 0 | ReturnPointDTO returnPoint = new ReturnPointDTO(nodeName); |
1060 | 0 | returnToPreviousNode(annotation, returnPoint); |
1061 | 0 | } |
1062 | ||
1063 | /** | |
1064 | * Performs the 'returnToPrevious' action on the document this WorkflowDocument represents. If this is a new document, | |
1065 | * the document is created first. | |
1066 | * @param annotation the message to log for the action | |
1067 | * @param ReturnPointDTO the node to return to | |
1068 | * @throws WorkflowException in case an error occurs returning to previous node | |
1069 | * @see WorkflowDocumentActions#returnDocumentToPreviousNode(UserIdDTO, RouteHeaderDTO, ReturnPointDTO, String) | |
1070 | */ | |
1071 | public void returnToPreviousNode(String annotation, ReturnPointDTO returnPoint) throws WorkflowException { | |
1072 | 0 | createDocumentIfNeccessary(); |
1073 | 0 | routeHeader = getWorkflowDocumentActions().returnDocumentToPreviousNode(principalId, getRouteHeader(), returnPoint, annotation); |
1074 | 0 | documentContentDirty = true; |
1075 | 0 | } |
1076 | ||
1077 | /** | |
1078 | * Moves the document from a current node in it's route to another node. If this is a new document, | |
1079 | * the document is created first. | |
1080 | * @param MovePointDTO VO representing the node at which to start, and the number of steps to move (negative steps is reverse) | |
1081 | * @param annotation the message to log for the action | |
1082 | * @throws WorkflowException in case an error occurs moving the document | |
1083 | * @see WorkflowDocumentActions#moveDocument(UserIdDTO, RouteHeaderDTO, MovePointDTO, String) | |
1084 | */ | |
1085 | public void moveDocument(MovePointDTO movePoint, String annotation) throws WorkflowException { | |
1086 | 0 | createDocumentIfNeccessary(); |
1087 | 0 | routeHeader = getWorkflowDocumentActions().moveDocument(principalId, getRouteHeader(), movePoint, annotation); |
1088 | 0 | documentContentDirty = true; |
1089 | 0 | } |
1090 | ||
1091 | /** | |
1092 | * Returns the route node instances that have been created so far during the life of this document. This includes | |
1093 | * all previous instances which have already been processed and are no longer active. | |
1094 | * @return the route node instances that have been created so far during the life of this document | |
1095 | * @throws WorkflowException if there is an error getting the route node instances for the document | |
1096 | * @see WorkflowUtility#getDocumentRouteNodeInstances(Long) | |
1097 | */ | |
1098 | public RouteNodeInstanceDTO[] getRouteNodeInstances() throws WorkflowException { | |
1099 | 0 | return getWorkflowUtility().getDocumentRouteNodeInstances(getDocumentId()); |
1100 | } | |
1101 | ||
1102 | /** | |
1103 | * Returns Array of Route Nodes Names that can be safely returned to using the 'returnToPreviousXXX' methods. | |
1104 | * Names are sorted in reverse chronological order. | |
1105 | * | |
1106 | * @return array of Route Nodes Names that can be safely returned to using the 'returnToPreviousXXX' methods | |
1107 | * @throws WorkflowException if an error occurs obtaining the names of the previous route nodes for this document | |
1108 | * @see WorkflowUtility#getPreviousRouteNodeNames(Long) | |
1109 | */ | |
1110 | public String[] getPreviousNodeNames() throws WorkflowException { | |
1111 | 0 | return getWorkflowUtility().getPreviousRouteNodeNames(getDocumentId()); |
1112 | } | |
1113 | ||
1114 | /** | |
1115 | * Returns a document detail VO representing the route header along with action requests, actions taken, | |
1116 | * and route node instances. | |
1117 | * @return Returns a document detail VO representing the route header along with action requests, actions taken, and route node instances. | |
1118 | * @throws WorkflowException | |
1119 | */ | |
1120 | public DocumentDetailDTO getDetail() throws WorkflowException { | |
1121 | 0 | return getWorkflowUtility().getDocumentDetail(getDocumentId()); |
1122 | } | |
1123 | ||
1124 | /** | |
1125 | * Saves the given DocumentContentVO for this document. | |
1126 | * @param documentContent document content VO to store for this document | |
1127 | * @since 2.3 | |
1128 | * @see WorkflowDocumentActions#saveDocumentContent(DocumentContentDTO) | |
1129 | */ | |
1130 | public DocumentContentDTO saveDocumentContent(DocumentContentDTO documentContent) throws WorkflowException { | |
1131 | 0 | if (documentContent.getDocumentId() == null) { |
1132 | 0 | throw new WorkflowException("Document Content does not have a valid document ID."); |
1133 | } | |
1134 | // important to check directly against getRouteHeader().getDocumentId() instead of just getDocumentId() because saveDocumentContent | |
1135 | // is called from createDocumentIfNeccessary which is called from getDocumentId(). If that method was used, we would have an infinite loop. | |
1136 | 0 | if (!documentContent.getDocumentId().equals(getRouteHeader().getDocumentId())) { |
1137 | 0 | throw new WorkflowException("Attempted to save content on this document with an invalid document id of " + documentContent.getDocumentId()); |
1138 | } | |
1139 | 0 | DocumentContentDTO newDocumentContent = getWorkflowDocumentActions().saveDocumentContent(documentContent); |
1140 | 0 | this.documentContent = new ModifiableDocumentContentDTO(newDocumentContent); |
1141 | 0 | documentContentDirty = false; |
1142 | 0 | return this.documentContent; |
1143 | } | |
1144 | ||
1145 | public void placeInExceptionRouting(String annotation) throws WorkflowException { | |
1146 | 0 | createDocumentIfNeccessary(); |
1147 | 0 | routeHeader = getWorkflowDocumentActions().placeInExceptionRouting(principalId, getRouteHeader(), annotation); |
1148 | 0 | documentContentDirty = true; |
1149 | 0 | } |
1150 | ||
1151 | /** | |
1152 | * Returns a list of NoteVO representing the notes on the document | |
1153 | * @return a list of NoteVO representing the notes on the document | |
1154 | * @see RouteHeaderDTO#getNotes() | |
1155 | */ | |
1156 | public List<NoteDTO> getNoteList(){ | |
1157 | 0 | List<NoteDTO> notesList = new ArrayList<NoteDTO>(); |
1158 | 0 | NoteDTO[] notes = routeHeader.getNotes(); |
1159 | 0 | if (notes != null){ |
1160 | 0 | for (int i=0; i<notes.length; i++){ |
1161 | 0 | if (! isDeletedNote(notes[i])){ |
1162 | 0 | notesList.add(notes[i]); |
1163 | } | |
1164 | } | |
1165 | } | |
1166 | 0 | return notesList; |
1167 | } | |
1168 | ||
1169 | /** | |
1170 | * Deletes a note from the document. The deletion is deferred until the next time the document is committed (via an action). | |
1171 | * @param noteVO the note to remove from the document | |
1172 | */ | |
1173 | public void deleteNote(NoteDTO noteVO){ | |
1174 | 0 | if (noteVO != null && noteVO.getNoteId()!=null){ |
1175 | 0 | NoteDTO noteToDelete = new NoteDTO(); |
1176 | 0 | noteToDelete.setNoteId(new Long(noteVO.getNoteId().longValue())); |
1177 | /*noteToDelete.setDocumentId(noteVO.getDocumentId()); | |
1178 | noteToDelete.setNoteAuthorWorkflowId(noteVO.getNoteAuthorWorkflowId()); | |
1179 | noteToDelete.setNoteCreateDate(noteVO.getNoteCreateDate()); | |
1180 | noteToDelete.setNoteText(noteVO.getNoteText()); | |
1181 | noteToDelete.setLockVerNbr(noteVO.getLockVerNbr());*/ | |
1182 | 0 | increaseNotesToDeleteArraySizeByOne(); |
1183 | 0 | routeHeader.getNotesToDelete()[routeHeader.getNotesToDelete().length - 1]=noteToDelete; |
1184 | } | |
1185 | 0 | } |
1186 | ||
1187 | /** | |
1188 | * 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). | |
1189 | * @param noteVO the note to update | |
1190 | */ | |
1191 | public void updateNote (NoteDTO noteVO){ | |
1192 | 0 | boolean isUpdateNote = false; |
1193 | 0 | if (noteVO != null){ |
1194 | 0 | NoteDTO[] notes = routeHeader.getNotes(); |
1195 | 0 | NoteDTO copyNote = new NoteDTO(); |
1196 | 0 | if (noteVO.getNoteId() != null){ |
1197 | 0 | copyNote.setNoteId(new Long(noteVO.getNoteId().longValue())); |
1198 | } | |
1199 | ||
1200 | 0 | if (noteVO.getDocumentId() != null){ |
1201 | 0 | copyNote.setDocumentId(noteVO.getDocumentId()); |
1202 | } else { | |
1203 | 0 | copyNote.setDocumentId(routeHeader.getDocumentId()); |
1204 | } | |
1205 | ||
1206 | 0 | if (noteVO.getNoteAuthorWorkflowId() != null){ |
1207 | 0 | copyNote.setNoteAuthorWorkflowId(new String(noteVO.getNoteAuthorWorkflowId())); |
1208 | } else { | |
1209 | 0 | copyNote.setNoteAuthorWorkflowId(principalId.toString()) ; |
1210 | } | |
1211 | ||
1212 | 0 | if (noteVO.getNoteCreateDate() != null){ |
1213 | 0 | Calendar cal = Calendar.getInstance(); |
1214 | 0 | cal.setTimeInMillis(noteVO.getNoteCreateDate().getTimeInMillis()); |
1215 | 0 | copyNote.setNoteCreateDate(cal); |
1216 | 0 | } else { |
1217 | 0 | copyNote.setNoteCreateDate(Calendar.getInstance()); |
1218 | } | |
1219 | ||
1220 | 0 | if (noteVO.getNoteText() != null){ |
1221 | 0 | copyNote.setNoteText(new String(noteVO.getNoteText())); |
1222 | } | |
1223 | 0 | if (noteVO.getLockVerNbr() != null){ |
1224 | 0 | copyNote.setLockVerNbr(new Integer(noteVO.getLockVerNbr().intValue())); |
1225 | } | |
1226 | 0 | if (notes != null){ |
1227 | 0 | for (int i=0; i<notes.length; i++){ |
1228 | 0 | if (notes[i].getNoteId()!= null && notes[i].getNoteId().equals(copyNote.getNoteId())){ |
1229 | 0 | notes[i] = copyNote; |
1230 | 0 | isUpdateNote = true; |
1231 | 0 | break; |
1232 | } | |
1233 | } | |
1234 | } | |
1235 | // add new note to the notes array | |
1236 | 0 | if (! isUpdateNote){ |
1237 | 0 | copyNote.setNoteId(null); |
1238 | 0 | increaseNotesArraySizeByOne(); |
1239 | 0 | routeHeader.getNotes()[routeHeader.getNotes().length-1]= copyNote; |
1240 | } | |
1241 | } | |
1242 | 0 | } |
1243 | ||
1244 | /** | |
1245 | * Sets a variable on the document. The assignment is deferred until the next time the document is committed (via an action). | |
1246 | * @param name name of the variable | |
1247 | * @param value value of the variable | |
1248 | */ | |
1249 | public void setVariable(String name, String value) throws WorkflowException { | |
1250 | 0 | createDocumentIfNeccessary(); |
1251 | 0 | getRouteHeader().setVariable(name, value); |
1252 | 0 | } |
1253 | ||
1254 | /** | |
1255 | * Gets the value of a variable on the document, creating the document first if it does not exist. | |
1256 | * @param name variable name | |
1257 | * @return variable value | |
1258 | */ | |
1259 | public String getVariable(String name) throws WorkflowException { | |
1260 | 0 | createDocumentIfNeccessary(); |
1261 | 0 | return getRouteHeader().getVariable(name); |
1262 | } | |
1263 | ||
1264 | /** | |
1265 | * | |
1266 | * Tells workflow that the current the document is constructed as will receive all future requests routed to them | |
1267 | * disregarding any force action flags set on the action request. Uses the setVariable method behind the seens so | |
1268 | * an action needs taken on the document to set this state on the document. | |
1269 | * | |
1270 | * @throws WorkflowException | |
1271 | */ | |
1272 | public void setReceiveFutureRequests() throws WorkflowException { | |
1273 | 0 | WorkflowUtility workflowUtility = getWorkflowUtility(); |
1274 | 0 | this.setVariable(workflowUtility.getFutureRequestsKey(principalId), workflowUtility.getReceiveFutureRequestsValue()); |
1275 | 0 | } |
1276 | ||
1277 | /** | |
1278 | * Tell workflow that the current document is constructed as will not receive any future requests routed to them | |
1279 | * disregarding any force action flags set on action requests. Uses the setVariable method behind the scenes so | |
1280 | * an action needs taken on the document to set this state on the document. | |
1281 | * | |
1282 | * @throws WorkflowException | |
1283 | */ | |
1284 | public void setDoNotReceiveFutureRequests() throws WorkflowException { | |
1285 | 0 | WorkflowUtility workflowUtility = getWorkflowUtility(); |
1286 | 0 | this.setVariable(workflowUtility.getFutureRequestsKey(principalId), workflowUtility.getDoNotReceiveFutureRequestsValue()); |
1287 | 0 | } |
1288 | ||
1289 | /** | |
1290 | * Clears any state set on the document regarding a user receiving or not receiving action requests. Uses the setVariable method | |
1291 | * behind the seens so an action needs taken on the document to set this state on the document. | |
1292 | * | |
1293 | * @throws WorkflowException | |
1294 | */ | |
1295 | public void setClearFutureRequests() throws WorkflowException { | |
1296 | 0 | WorkflowUtility workflowUtility = getWorkflowUtility(); |
1297 | 0 | this.setVariable(workflowUtility.getFutureRequestsKey(principalId), workflowUtility.getClearFutureRequestsValue()); |
1298 | 0 | } |
1299 | ||
1300 | /** | |
1301 | * Deletes the note of with the same id as that of the argument on the document. | |
1302 | * @param noteVO the note to test for deletion | |
1303 | * @return whether the note is already marked for deletion. | |
1304 | */ | |
1305 | private boolean isDeletedNote(NoteDTO noteVO) { | |
1306 | 0 | NoteDTO[] notesToDelete = routeHeader.getNotesToDelete(); |
1307 | 0 | if (notesToDelete != null){ |
1308 | 0 | for (int i=0; i<notesToDelete.length; i++){ |
1309 | 0 | if (notesToDelete[i].getNoteId().equals(noteVO.getNoteId())){ |
1310 | 0 | return true; |
1311 | } | |
1312 | } | |
1313 | } | |
1314 | 0 | return false; |
1315 | } | |
1316 | ||
1317 | /** | |
1318 | * Increases the size of the routeHeader notes VO array | |
1319 | */ | |
1320 | private void increaseNotesArraySizeByOne() { | |
1321 | NoteDTO[] tempArray; | |
1322 | 0 | NoteDTO[] notes = routeHeader.getNotes(); |
1323 | 0 | if (notes == null){ |
1324 | 0 | tempArray = new NoteDTO[1]; |
1325 | } else { | |
1326 | 0 | tempArray = new NoteDTO[notes.length + 1]; |
1327 | 0 | for (int i=0; i<notes.length; i++){ |
1328 | 0 | tempArray[i] = notes[i]; |
1329 | } | |
1330 | } | |
1331 | 0 | routeHeader.setNotes(tempArray); |
1332 | 0 | } |
1333 | ||
1334 | /** | |
1335 | * Increases the size of the routeHeader notesToDelete VO array | |
1336 | */ | |
1337 | private void increaseNotesToDeleteArraySizeByOne() { | |
1338 | NoteDTO[] tempArray; | |
1339 | 0 | NoteDTO[] notesToDelete = routeHeader.getNotesToDelete(); |
1340 | 0 | if (notesToDelete == null){ |
1341 | 0 | tempArray = new NoteDTO[1]; |
1342 | } else { | |
1343 | 0 | tempArray = new NoteDTO[notesToDelete.length + 1]; |
1344 | 0 | for (int i=0; i<notesToDelete.length; i++){ |
1345 | 0 | tempArray[i] = notesToDelete[i]; |
1346 | } | |
1347 | } | |
1348 | 0 | routeHeader.setNotesToDelete(tempArray); |
1349 | 0 | } |
1350 | ||
1351 | //add 1 link between 2 docs by DTO, double link added | |
1352 | public void addLinkedDocument(DocumentLinkDTO docLinkVO) throws WorkflowException{ | |
1353 | try{ | |
1354 | 0 | if(DocumentLinkDTO.checkDocLink(docLinkVO)) |
1355 | 0 | getWorkflowUtility().addDocumentLink(docLinkVO); |
1356 | } | |
1357 | 0 | catch(Exception e){ |
1358 | 0 | throw handleExceptionAsRuntime(e); |
1359 | 0 | } |
1360 | 0 | } |
1361 | ||
1362 | //get link from orgn doc to a specifc doc | |
1363 | public DocumentLinkDTO getLinkedDocument(DocumentLinkDTO docLinkVO) throws WorkflowException{ | |
1364 | try{ | |
1365 | 0 | if(DocumentLinkDTO.checkDocLink(docLinkVO)) |
1366 | 0 | return getWorkflowUtility().getLinkedDocument(docLinkVO); |
1367 | else | |
1368 | 0 | return null; |
1369 | } | |
1370 | 0 | catch(Exception e){ |
1371 | 0 | throw handleExceptionAsRuntime(e); |
1372 | } | |
1373 | } | |
1374 | ||
1375 | //get all links to orgn doc | |
1376 | public List<DocumentLinkDTO> getLinkedDocumentsByDocId(String documentId) throws WorkflowException{ | |
1377 | 0 | if(documentId == null) |
1378 | 0 | throw new WorkflowException("document Id is null"); |
1379 | try{ | |
1380 | 0 | return getWorkflowUtility().getLinkedDocumentsByDocId(documentId); |
1381 | } | |
1382 | 0 | catch (Exception e) { |
1383 | 0 | throw handleExceptionAsRuntime(e); |
1384 | } | |
1385 | } | |
1386 | ||
1387 | //remove all links from orgn: double links removed | |
1388 | public void removeLinkedDocuments(String docId) throws WorkflowException{ | |
1389 | ||
1390 | 0 | if(docId == null) |
1391 | 0 | throw new WorkflowException("doc id is null"); |
1392 | ||
1393 | try{ | |
1394 | 0 | getWorkflowUtility().deleteDocumentLinksByDocId(docId); |
1395 | } | |
1396 | 0 | catch (Exception e) { |
1397 | 0 | throw handleExceptionAsRuntime(e); |
1398 | 0 | } |
1399 | 0 | } |
1400 | ||
1401 | //remove link between 2 docs, double link removed | |
1402 | public void removeLinkedDocument(DocumentLinkDTO docLinkVO) throws WorkflowException{ | |
1403 | ||
1404 | try{ | |
1405 | 0 | if(DocumentLinkDTO.checkDocLink(docLinkVO)) |
1406 | 0 | getWorkflowUtility().deleteDocumentLink(docLinkVO); |
1407 | } | |
1408 | 0 | catch(Exception e){ |
1409 | 0 | throw handleExceptionAsRuntime(e); |
1410 | 0 | } |
1411 | 0 | } |
1412 | ||
1413 | } |