001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.kuali.core.workflow.service.impl;
017:
018: import java.util.ArrayList;
019: import java.util.Iterator;
020: import java.util.List;
021:
022: import org.apache.commons.lang.StringUtils;
023: import org.kuali.core.bo.AdHocRouteRecipient;
024: import org.kuali.core.bo.user.UniversalUser;
025: import org.kuali.core.exceptions.UnknownDocumentIdException;
026: import org.kuali.core.util.GlobalVariables;
027: import org.kuali.core.util.Timer;
028: import org.kuali.core.workflow.service.KualiWorkflowDocument;
029: import org.kuali.core.workflow.service.KualiWorkflowInfo;
030: import org.kuali.core.workflow.service.WorkflowDocumentService;
031: import org.springframework.transaction.annotation.Transactional;
032:
033: import edu.iu.uis.eden.EdenConstants;
034: import edu.iu.uis.eden.KEWServiceLocator;
035: import edu.iu.uis.eden.clientapp.WorkflowInfo;
036: import edu.iu.uis.eden.clientapp.vo.NetworkIdVO;
037: import edu.iu.uis.eden.clientapp.vo.RouteNodeInstanceVO;
038: import edu.iu.uis.eden.clientapp.vo.UserIdVO;
039: import edu.iu.uis.eden.clientapp.vo.WorkgroupNameIdVO;
040: import edu.iu.uis.eden.exception.DocumentTypeNotFoundException;
041: import edu.iu.uis.eden.exception.EdenUserNotFoundException;
042: import edu.iu.uis.eden.exception.InvalidActionTakenException;
043: import edu.iu.uis.eden.exception.InvalidWorkgroupException;
044: import edu.iu.uis.eden.exception.WorkflowException;
045:
046: /**
047: * This class is the implementation of the WorkflowDocumentService, which makes use of OneStart Workflow.
048: */
049: @Transactional
050: public class WorkflowDocumentServiceImpl implements
051: WorkflowDocumentService {
052: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
053: .getLogger(WorkflowDocumentServiceImpl.class);
054:
055: private KualiWorkflowInfo workflowInfoService;
056:
057: public boolean workflowDocumentExists(String documentHeaderId) {
058: boolean exists = false;
059:
060: if (StringUtils.isBlank(documentHeaderId)) {
061: throw new IllegalArgumentException(
062: "invalid (blank) documentHeaderId");
063: }
064:
065: Long routeHeaderId = null;
066: try {
067: routeHeaderId = new Long(documentHeaderId);
068: } catch (NumberFormatException e) {
069: throw new IllegalArgumentException("cannot convert '"
070: + documentHeaderId + "' into a Long");
071: }
072:
073: exists = workflowInfoService.routeHeaderExists(routeHeaderId);
074:
075: return exists;
076: }
077:
078: /**
079: * @see org.kuali.core.workflow.service.WorkflowDocumentService#createWorkflowDocument(java.lang.String,
080: * edu.iu.uis.eden.user.WorkflowUser)
081: */
082: public KualiWorkflowDocument createWorkflowDocument(
083: String documentTypeId, UniversalUser universalUser)
084: throws WorkflowException {
085: Timer t0 = new Timer("createWorkflowDocument");
086:
087: if (StringUtils.isBlank(documentTypeId)) {
088: throw new IllegalArgumentException(
089: "invalid (blank) documentTypeId");
090: }
091: if (universalUser == null) {
092: throw new IllegalArgumentException(
093: "invalid (null) universalUser");
094: }
095:
096: if ((null == universalUser)
097: || StringUtils.isBlank(universalUser
098: .getPersonUserIdentifier())) {
099: throw new IllegalArgumentException(
100: "invalid (empty) authenticationUserId");
101: }
102:
103: if (LOG.isDebugEnabled()) {
104: LOG.debug("creating workflowDoc(" + documentTypeId + ","
105: + universalUser.getPersonUserIdentifier() + ")");
106: }
107:
108: KualiWorkflowDocument document = new KualiWorkflowDocumentImpl(
109: getUserIdVO(universalUser), documentTypeId);
110:
111: // workflow doesn't complain about invalid docTypes until the first call to getRouteHeaderId, but the rest of our code
112: // assumes that you get the exception immediately upon trying to create a document of that invalid type
113: //
114: // and it throws the generic WorkflowException, apparently, instead of the more specific DocumentTypeNotFoundException,
115: // so as long as I'm here I'll do that conversion as well
116: try {
117: document.getRouteHeaderId();
118: } catch (WorkflowException e) {
119: if (e.getMessage().indexOf(
120: "Could not locate the given document type name") != -1) {
121: throw new DocumentTypeNotFoundException(
122: "unknown document type '" + documentTypeId
123: + "'");
124: } else {
125: throw e;
126: }
127: }
128:
129: t0.log();
130:
131: return document;
132: }
133:
134: /**
135: * @see org.kuali.core.workflow.service.WorkflowDocumentService#createWorkflowDocument(java.lang.Long,
136: * edu.iu.uis.eden.user.WorkflowUser)
137: */
138: public KualiWorkflowDocument createWorkflowDocument(
139: Long documentHeaderId, UniversalUser user)
140: throws WorkflowException {
141: if (documentHeaderId == null) {
142: throw new IllegalArgumentException(
143: "invalid (null) documentHeaderId");
144: }
145: if (user == null) {
146: throw new IllegalArgumentException(
147: "invalid (null) workflowUser");
148: } else if (StringUtils.isEmpty(user.getPersonUserIdentifier())) {
149: throw new IllegalArgumentException(
150: "invalid (empty) workflowUser");
151: }
152:
153: if (LOG.isDebugEnabled()) {
154: LOG.debug("retrieving flexDoc(" + documentHeaderId + ","
155: + user.getPersonUserIdentifier() + ")");
156: }
157:
158: KualiWorkflowDocument document = new KualiWorkflowDocumentImpl(
159: getUserIdVO(user), documentHeaderId);
160: if (document.getRouteHeader() == null) {
161: throw new UnknownDocumentIdException(
162: "unable to locate document with documentHeaderId '"
163: + documentHeaderId + "'");
164: }
165: return document;
166: }
167:
168: /**
169: * @see org.kuali.core.workflow.service.WorkflowDocumentService#acknowledge(edu.iu.uis.eden.routetemplate.FlexDoc)
170: */
171: public void acknowledge(KualiWorkflowDocument workflowDocument,
172: String annotation, List adHocRecipients)
173: throws WorkflowException {
174: if (LOG.isDebugEnabled()) {
175: LOG.debug("acknowleding flexDoc("
176: + workflowDocument.getRouteHeaderId() + ",'"
177: + annotation + "')");
178: }
179:
180: handleAdHocRouteRequests(workflowDocument, annotation,
181: filterAdHocRecipients(adHocRecipients, new String[] {
182: EdenConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ,
183: EdenConstants.ACTION_REQUEST_FYI_REQ }));
184: workflowDocument.acknowledge(annotation);
185: }
186:
187: /**
188: * @see org.kuali.core.workflow.service.WorkflowDocumentService#approve(edu.iu.uis.eden.routetemplate.FlexDoc)
189: */
190: public void approve(KualiWorkflowDocument workflowDocument,
191: String annotation, List adHocRecipients)
192: throws WorkflowException {
193: if (LOG.isDebugEnabled()) {
194: LOG.debug("approving flexDoc("
195: + workflowDocument.getRouteHeaderId() + ",'"
196: + annotation + "')");
197: }
198:
199: handleAdHocRouteRequests(workflowDocument, annotation,
200: filterAdHocRecipients(adHocRecipients, new String[] {
201: EdenConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ,
202: EdenConstants.ACTION_REQUEST_FYI_REQ,
203: EdenConstants.ACTION_REQUEST_APPROVE_REQ }));
204: workflowDocument.approve(annotation);
205: }
206:
207: /**
208: * @see org.kuali.core.workflow.service.WorkflowDocumentService#superUserApprove(org.kuali.core.workflow.service.KualiWorkflowDocument,
209: * java.lang.String)
210: */
211: public void super UserApprove(
212: KualiWorkflowDocument workflowDocument, String annotation)
213: throws WorkflowException {
214: LOG.info("super user approve flexDoc("
215: + workflowDocument.getRouteHeaderId() + ",'"
216: + annotation + "')");
217: workflowDocument.super UserApprove(annotation);
218: }
219:
220: /**
221: * @see org.kuali.core.workflow.service.WorkflowDocumentService#superUserCancel(org.kuali.core.workflow.service.KualiWorkflowDocument,
222: * java.lang.String)
223: */
224: public void super UserCancel(KualiWorkflowDocument workflowDocument,
225: String annotation) throws WorkflowException {
226: LOG.info("super user cancel flexDoc("
227: + workflowDocument.getRouteHeaderId() + ",'"
228: + annotation + "')");
229: workflowDocument.super UserCancel(annotation);
230: }
231:
232: /**
233: * @see org.kuali.core.workflow.service.WorkflowDocumentService#superUserDisapprove(org.kuali.core.workflow.service.KualiWorkflowDocument,
234: * java.lang.String)
235: */
236: public void super UserDisapprove(
237: KualiWorkflowDocument workflowDocument, String annotation)
238: throws WorkflowException {
239: LOG.info("super user approve flexDoc("
240: + workflowDocument.getRouteHeaderId() + ",'"
241: + annotation + "')");
242: workflowDocument.super UserDisapprove(annotation);
243: }
244:
245: /**
246: * @see org.kuali.core.workflow.service.WorkflowDocumentService#blanketApprove(edu.iu.uis.eden.routetemplate.FlexDoc)
247: */
248: public void blanketApprove(KualiWorkflowDocument workflowDocument,
249: String annotation, List adHocRecipients)
250: throws WorkflowException {
251: if (LOG.isDebugEnabled()) {
252: LOG.debug("blanket approving flexDoc("
253: + workflowDocument.getRouteHeaderId() + ",'"
254: + annotation + "')");
255: }
256:
257: handleAdHocRouteRequests(workflowDocument, annotation,
258: filterAdHocRecipients(adHocRecipients, new String[] {
259: EdenConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ,
260: EdenConstants.ACTION_REQUEST_FYI_REQ }));
261: workflowDocument.blanketApprove(annotation);
262: }
263:
264: /**
265: * @see org.kuali.core.workflow.service.WorkflowDocumentService#cancel(edu.iu.uis.eden.routetemplate.FlexDoc)
266: */
267: public void cancel(KualiWorkflowDocument workflowDocument,
268: String annotation) throws WorkflowException {
269: if (LOG.isDebugEnabled()) {
270: LOG.debug("canceling flexDoc("
271: + workflowDocument.getRouteHeaderId() + ",'"
272: + annotation + "')");
273: }
274:
275: workflowDocument.cancel(annotation);
276: }
277:
278: /**
279: * @see org.kuali.core.workflow.service.WorkflowDocumentService#clearFyi(edu.iu.uis.eden.routetemplate.FlexDoc)
280: */
281: public void clearFyi(KualiWorkflowDocument workflowDocument,
282: List adHocRecipients) throws WorkflowException {
283: if (LOG.isDebugEnabled()) {
284: LOG.debug("clearing FYI for flexDoc("
285: + workflowDocument.getRouteHeaderId() + ")");
286: }
287:
288: handleAdHocRouteRequests(
289: workflowDocument,
290: "",
291: filterAdHocRecipients(
292: adHocRecipients,
293: new String[] { EdenConstants.ACTION_REQUEST_FYI_REQ }));
294: workflowDocument.fyi();
295: }
296:
297: /**
298: * @see org.kuali.core.workflow.service.WorkflowDocumentService#sendWorkflowNotification(org.kuali.core.workflow.service.KualiWorkflowDocument, java.lang.String, java.util.List)
299: */
300: public void sendWorkflowNotification(
301: KualiWorkflowDocument workflowDocument, String annotation,
302: List adHocRecipients) throws WorkflowException {
303: if (LOG.isDebugEnabled()) {
304: LOG.debug("sending FYI for flexDoc("
305: + workflowDocument.getRouteHeaderId() + ")");
306: }
307:
308: handleAdHocRouteRequests(workflowDocument, annotation,
309: adHocRecipients);
310: }
311:
312: /**
313: * @see org.kuali.core.workflow.service.WorkflowDocumentService#disapprove(edu.iu.uis.eden.routetemplate.FlexDoc)
314: */
315: public void disapprove(KualiWorkflowDocument workflowDocument,
316: String annotation) throws WorkflowException {
317: if (LOG.isDebugEnabled()) {
318: LOG.debug("disapproving flexDoc("
319: + workflowDocument.getRouteHeaderId() + ",'"
320: + annotation + "')");
321: }
322:
323: workflowDocument.disapprove(annotation);
324: }
325:
326: /**
327: * @see org.kuali.core.workflow.service.WorkflowDocumentService#route(edu.iu.uis.eden.routetemplate.FlexDoc)
328: */
329: public void route(KualiWorkflowDocument workflowDocument,
330: String annotation, List adHocRecipients)
331: throws WorkflowException {
332: if (LOG.isDebugEnabled()) {
333: LOG.debug("routing flexDoc("
334: + workflowDocument.getRouteHeaderId() + ",'"
335: + annotation + "')");
336: }
337:
338: handleAdHocRouteRequests(workflowDocument, annotation,
339: filterAdHocRecipients(adHocRecipients, new String[] {
340: EdenConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ,
341: EdenConstants.ACTION_REQUEST_FYI_REQ,
342: EdenConstants.ACTION_REQUEST_APPROVE_REQ }));
343: workflowDocument.routeDocument(annotation);
344: }
345:
346: /**
347: * @see org.kuali.core.workflow.service.WorkflowDocumentService#save(org.kuali.core.workflow.service.KualiWorkflowDocument, java.lang.String)
348: */
349: public void save(KualiWorkflowDocument workflowDocument,
350: String annotation) throws WorkflowException {
351: if (workflowDocument.isStandardSaveAllowed()) {
352: if (LOG.isDebugEnabled()) {
353: LOG.debug("saving flexDoc("
354: + workflowDocument.getRouteHeaderId() + ",'"
355: + annotation + "')");
356: }
357: workflowDocument.saveDocument(annotation);
358: } else {
359: this .saveRoutingData(workflowDocument);
360: }
361: }
362:
363: /**
364: * @see org.kuali.core.workflow.service.WorkflowDocumentService#saveRoutingData(org.kuali.core.workflow.service.KualiWorkflowDocument)
365: */
366: public void saveRoutingData(KualiWorkflowDocument workflowDocument)
367: throws WorkflowException {
368: if (LOG.isDebugEnabled()) {
369: LOG.debug("saving flexDoc("
370: + workflowDocument.getRouteHeaderId() + ")");
371: }
372:
373: workflowDocument.saveRoutingData();
374: }
375:
376: /**
377: * @see org.kuali.core.workflow.service.WorkflowDocumentService#getCurrentRouteLevelName(org.kuali.core.workflow.service.KualiWorkflowDocument)
378: */
379: public String getCurrentRouteLevelName(
380: KualiWorkflowDocument workflowDocument)
381: throws WorkflowException {
382: if (LOG.isDebugEnabled()) {
383: LOG.debug("getting current route level name for flexDoc("
384: + workflowDocument.getRouteHeaderId());
385: }
386: // return KEWServiceLocator.getRouteHeaderService().getRouteHeader(workflowDocument.getRouteHeaderId()).getCurrentRouteLevelName();
387: KualiWorkflowDocument freshCopyWorkflowDoc = createWorkflowDocument(
388: workflowDocument.getRouteHeaderId(), GlobalVariables
389: .getUserSession().getUniversalUser());
390: return freshCopyWorkflowDoc.getCurrentRouteNodeNames();
391: }
392:
393: /**
394: * Convenience method for generating ad hoc requests for a given document
395: *
396: * @param flexDoc
397: * @param annotation
398: * @param adHocRecipients
399: * @throws InvalidActionTakenException
400: * @throws InvalidRouteTypeException
401: * @throws EdenUserNotFoundException
402: * @throws InvalidActionRequestException
403: * @throws EdenException
404: * @throws InvalidWorkgroupException
405: */
406: private void handleAdHocRouteRequests(
407: KualiWorkflowDocument workflowDocument, String annotation,
408: List adHocRecipients) throws WorkflowException {
409:
410: if (adHocRecipients != null && adHocRecipients.size() > 0) {
411: String currentNode = null;
412: String[] currentNodes = workflowDocument.getNodeNames();
413: if (currentNodes.length == 0) {
414: WorkflowInfo workflowInfo = new WorkflowInfo();
415: RouteNodeInstanceVO[] nodes = workflowInfo
416: .getTerminalNodeInstances(workflowDocument
417: .getRouteHeaderId());
418: currentNodes = new String[nodes.length];
419: for (int nodeIndex = 0; nodeIndex < nodes.length; nodeIndex++) {
420: currentNodes[nodeIndex] = nodes[nodeIndex]
421: .getName();
422: }
423: }
424: // for now just pick a node and go with it...
425: for (int i = 0; i < currentNodes.length; i++) {
426: currentNode = currentNodes[i];
427: }
428:
429: for (Iterator iter = adHocRecipients.iterator(); iter
430: .hasNext();) {
431: AdHocRouteRecipient recipient = (AdHocRouteRecipient) iter
432: .next();
433: if (StringUtils.isNotEmpty(recipient.getId())) {
434: if (AdHocRouteRecipient.PERSON_TYPE
435: .equals(recipient.getType())) {
436: // TODO make the 1 a constant
437: workflowDocument
438: .appSpecificRouteDocumentToUser(
439: recipient.getActionRequested(),
440: currentNode, 0, annotation,
441: new NetworkIdVO(recipient
442: .getId()), "", true);
443: } else {
444: // TODO is this recripientId truly a workgroup name??
445: workflowDocument
446: .appSpecificRouteDocumentToWorkgroup(
447: recipient.getActionRequested(),
448: currentNode, 0, annotation,
449: new WorkgroupNameIdVO(recipient
450: .getId()), "", true);
451: }
452: }
453: }
454: }
455: }
456:
457: /**
458: * Convenience method to filter out any ad hoc recipients that should not be allowed given the action requested of the user that
459: * is taking action on the document
460: *
461: * @param adHocRecipients
462: */
463: private List filterAdHocRecipients(List adHocRecipients,
464: String[] validTypes) {
465: // now filter out any but ack or fyi from the ad hoc list
466: List realAdHocRecipients = new ArrayList();
467: if (adHocRecipients != null) {
468: for (Iterator iter = adHocRecipients.iterator(); iter
469: .hasNext();) {
470: AdHocRouteRecipient proposedRecipient = (AdHocRouteRecipient) iter
471: .next();
472: if (StringUtils.isNotBlank(proposedRecipient
473: .getActionRequested())) {
474: for (int i = 0; i < validTypes.length; i++) {
475: if (validTypes[i].equals(proposedRecipient
476: .getActionRequested())) {
477: realAdHocRecipients.add(proposedRecipient);
478: }
479: }
480: }
481: }
482: }
483: return realAdHocRecipients;
484: }
485:
486: private UserIdVO getUserIdVO(UniversalUser user) {
487: return new NetworkIdVO(user.getPersonUserIdentifier());
488: }
489:
490: public void setWorkflowInfoService(
491: KualiWorkflowInfo workflowInfoService) {
492: this .workflowInfoService = workflowInfoService;
493: }
494:
495: public KualiWorkflowInfo getWorkflowInfoService() {
496: return workflowInfoService;
497: }
498: }
|