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.service.impl;
017:
018: import java.lang.reflect.Constructor;
019: import java.lang.reflect.InvocationTargetException;
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Date;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.List;
026:
027: import org.apache.commons.lang.StringUtils;
028: import org.kuali.RiceConstants;
029: import org.kuali.core.bo.AdHocRouteRecipient;
030: import org.kuali.core.bo.DocumentHeader;
031: import org.kuali.core.bo.Note;
032: import org.kuali.core.bo.PersistableBusinessObject;
033: import org.kuali.core.bo.user.UniversalUser;
034: import org.kuali.core.dao.DocumentDao;
035: import org.kuali.core.dao.DocumentHeaderDao;
036: import org.kuali.core.document.Document;
037: import org.kuali.core.document.MaintenanceDocumentBase;
038: import org.kuali.core.document.authorization.DocumentActionFlags;
039: import org.kuali.core.document.authorization.DocumentAuthorizer;
040: import org.kuali.core.exceptions.DocumentAuthorizationException;
041: import org.kuali.core.exceptions.InactiveDocumentTypeAuthorizationException;
042: import org.kuali.core.exceptions.UnknownDocumentTypeException;
043: import org.kuali.core.exceptions.ValidationException;
044: import org.kuali.core.rule.event.ApproveDocumentEvent;
045: import org.kuali.core.rule.event.BlanketApproveDocumentEvent;
046: import org.kuali.core.rule.event.KualiDocumentEvent;
047: import org.kuali.core.rule.event.RouteDocumentEvent;
048: import org.kuali.core.rule.event.SaveDocumentEvent;
049: import org.kuali.core.rule.event.SaveEvent;
050: import org.kuali.core.service.BusinessObjectService;
051: import org.kuali.core.service.DateTimeService;
052: import org.kuali.core.service.DictionaryValidationService;
053: import org.kuali.core.service.DocumentAuthorizationService;
054: import org.kuali.core.service.DocumentService;
055: import org.kuali.core.service.DocumentTypeService;
056: import org.kuali.core.service.KualiRuleService;
057: import org.kuali.core.service.MaintenanceDocumentService;
058: import org.kuali.core.service.NoteService;
059: import org.kuali.core.util.GlobalVariables;
060: import org.kuali.core.util.ObjectUtils;
061: import org.kuali.core.util.Timer;
062: import org.kuali.core.workflow.service.KualiWorkflowDocument;
063: import org.kuali.core.workflow.service.WorkflowDocumentService;
064: import org.kuali.rice.config.ConfigurationException;
065: import org.springframework.dao.OptimisticLockingFailureException;
066: import org.springframework.transaction.annotation.Transactional;
067:
068: import edu.iu.uis.eden.exception.WorkflowException;
069:
070: /**
071: * This class is the service implementation for the Document structure. It contains all of the document level type of processing and
072: * calling back into documents for various centralization of functionality. This is the default, Kuali delivered implementation
073: * which utilizes OneStart Workflow.
074: */
075: @Transactional
076: public class DocumentServiceImpl implements DocumentService {
077: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
078: .getLogger(DocumentServiceImpl.class);
079: private DocumentHeaderDao documentHeaderDao;
080: private DocumentTypeService documentTypeService;
081: private DateTimeService dateTimeService;
082: private KualiRuleService kualiRuleService;
083: private DictionaryValidationService dictionaryValidationService;
084: private MaintenanceDocumentService maintenanceDocumentService;
085: private NoteService noteService;
086: protected WorkflowDocumentService workflowDocumentService;
087: protected BusinessObjectService businessObjectService;
088: protected DocumentAuthorizationService documentAuthorizationService;
089: protected DocumentDao documentDao;
090:
091: /**
092: * @see org.kuali.core.service.DocumentService#saveDocument(org.kuali.core.document.Document)
093: */
094: public Document saveDocument(Document document)
095: throws WorkflowException, ValidationException {
096: return saveDocument(document, SaveDocumentEvent.class);
097: }
098:
099: public Document saveDocument(Document document,
100: Class kualiDocumentEventClass) throws WorkflowException,
101: ValidationException {
102: checkForNulls(document);
103: if (kualiDocumentEventClass == null) {
104: throw new IllegalArgumentException(
105: "invalid (null) kualiDocumentEventClass");
106: }
107: // if event is not an instance of a SaveDocumentEvent or a SaveOnlyDocumentEvent
108: if (!SaveEvent.class.isAssignableFrom(kualiDocumentEventClass)) {
109: throw new ConfigurationException(
110: "The KualiDocumentEvent class '"
111: + kualiDocumentEventClass.getName()
112: + "' does not implement the class '"
113: + SaveEvent.class.getName() + "'");
114: }
115: // if (!getDocumentActionFlags(document).getCanSave()) {
116: // throw buildAuthorizationException("save", document);
117: // }
118: document.prepareForSave();
119: validateAndPersistDocumentAndSaveAdHocRoutingRecipients(
120: document, generateKualiDocumentEvent(document,
121: kualiDocumentEventClass));
122: prepareWorkflowDocument(document);
123: workflowDocumentService.save(document.getDocumentHeader()
124: .getWorkflowDocument(), null);
125: GlobalVariables.getUserSession().setWorkflowDocument(
126: document.getDocumentHeader().getWorkflowDocument());
127:
128: return document;
129: }
130:
131: private KualiDocumentEvent generateKualiDocumentEvent(
132: Document document, Class eventClass)
133: throws ConfigurationException {
134: String potentialErrorMessage = "Found error trying to generate Kuali Document Event using event class '"
135: + eventClass.getName()
136: + "' for document "
137: + document.getDocumentNumber();
138: try {
139: Constructor usableConstructor = null;
140: List<Object> paramList = null;
141: for (Constructor currentConstructor : eventClass
142: .getConstructors()) {
143: paramList = new ArrayList<Object>();
144: for (Class parameterClass : currentConstructor
145: .getParameterTypes()) {
146: if (Document.class.isAssignableFrom(parameterClass)) {
147: usableConstructor = currentConstructor;
148: paramList.add(document);
149: } else {
150: paramList.add(null);
151: }
152: }
153: if (ObjectUtils.isNotNull(usableConstructor)) {
154: break;
155: }
156: }
157: if (ObjectUtils.isNull(usableConstructor)) {
158: throw new RuntimeException(
159: "Cannot find a constructor for class '"
160: + eventClass.getName()
161: + "' that takes in a document parameter");
162: } else {
163: usableConstructor.newInstance(paramList.toArray());
164: return (KualiDocumentEvent) usableConstructor
165: .newInstance(paramList.toArray());
166: }
167: } catch (SecurityException e) {
168: throw new ConfigurationException(potentialErrorMessage, e);
169: } catch (IllegalArgumentException e) {
170: throw new ConfigurationException(potentialErrorMessage, e);
171: } catch (InstantiationException e) {
172: throw new ConfigurationException(potentialErrorMessage, e);
173: } catch (IllegalAccessException e) {
174: throw new ConfigurationException(potentialErrorMessage, e);
175: } catch (InvocationTargetException e) {
176: throw new ConfigurationException(potentialErrorMessage, e);
177: }
178: }
179:
180: /**
181: * @see org.kuali.core.service.DocumentService#routeDocument(org.kuali.core.document.Document, java.lang.String, java.util.List)
182: */
183: public Document routeDocument(Document document, String annotation,
184: List adHocRecipients) throws ValidationException,
185: WorkflowException {
186: checkForNulls(document);
187: if (!getDocumentActionFlags(document).getCanRoute()) {
188: throw buildAuthorizationException("route", document);
189: }
190: document.prepareForSave();
191: validateAndPersistDocument(document, new RouteDocumentEvent(
192: document));
193: prepareWorkflowDocument(document);
194: workflowDocumentService.route(document.getDocumentHeader()
195: .getWorkflowDocument(), annotation, adHocRecipients);
196: GlobalVariables.getUserSession().setWorkflowDocument(
197: document.getDocumentHeader().getWorkflowDocument());
198: businessObjectService.delete(document.getAdHocRoutePersons());
199: businessObjectService
200: .delete(document.getAdHocRouteWorkgroups());
201: return document;
202: }
203:
204: /**
205: * @see org.kuali.core.service.DocumentService#approveDocument(org.kuali.core.document.Document, java.lang.String,
206: * java.util.List)
207: */
208: public Document approveDocument(Document document,
209: String annotation, List adHocRecipients)
210: throws ValidationException, WorkflowException {
211: checkForNulls(document);
212: if (!getDocumentActionFlags(document).getCanApprove()) {
213: throw buildAuthorizationException("approve", document);
214: }
215: document.prepareForSave();
216: validateAndPersistDocument(document, new ApproveDocumentEvent(
217: document));
218: prepareWorkflowDocument(document);
219: workflowDocumentService.approve(document.getDocumentHeader()
220: .getWorkflowDocument(), annotation, adHocRecipients);
221: GlobalVariables.getUserSession().setWorkflowDocument(
222: document.getDocumentHeader().getWorkflowDocument());
223: return document;
224: }
225:
226: /**
227: * @see org.kuali.core.service.DocumentService#superUserApproveDocument(org.kuali.core.document.Document, java.lang.String)
228: */
229: public Document super UserApproveDocument(Document document,
230: String annotation) throws WorkflowException {
231: documentDao.save(document);
232: prepareWorkflowDocument(document);
233: workflowDocumentService.super UserApprove(document
234: .getDocumentHeader().getWorkflowDocument(), annotation);
235: GlobalVariables.getUserSession().setWorkflowDocument(
236: document.getDocumentHeader().getWorkflowDocument());
237: return document;
238: }
239:
240: /**
241: * @see org.kuali.core.service.DocumentService#superUserCancelDocument(org.kuali.core.document.Document, java.lang.String)
242: */
243: public Document super UserCancelDocument(Document document,
244: String annotation) throws WorkflowException {
245: documentDao.save(document);
246: prepareWorkflowDocument(document);
247: workflowDocumentService.super UserCancel(document
248: .getDocumentHeader().getWorkflowDocument(), annotation);
249: GlobalVariables.getUserSession().setWorkflowDocument(
250: document.getDocumentHeader().getWorkflowDocument());
251: return document;
252: }
253:
254: /**
255: * @see org.kuali.core.service.DocumentService#superUserCancelDocument(org.kuali.core.document.Document, java.lang.String)
256: */
257: public Document super UserDisapproveDocument(Document document,
258: String annotation) throws WorkflowException {
259: documentDao.save(document);
260: prepareWorkflowDocument(document);
261: workflowDocumentService.super UserDisapprove(document
262: .getDocumentHeader().getWorkflowDocument(), annotation);
263: GlobalVariables.getUserSession().setWorkflowDocument(
264: document.getDocumentHeader().getWorkflowDocument());
265: return document;
266: }
267:
268: /**
269: * @see org.kuali.core.service.DocumentService#disapproveDocument(org.kuali.core.document.Document, java.lang.String)
270: */
271: public Document disapproveDocument(Document document,
272: String annotation) throws Exception {
273: checkForNulls(document);
274: if (!getDocumentActionFlags(document).getCanDisapprove()) {
275: throw buildAuthorizationException("disapprove", document);
276: }
277:
278: Note note = createNoteFromDocument(document, annotation);
279: addNoteToDocument(document, note);
280:
281: //SAVE THE NOTE
282: //Note: This save logic is replicated here and in KualiDocumentAction, when to save (based on doc state) should be moved
283: // into a doc service method
284: noteService.save(note);
285:
286: prepareWorkflowDocument(document);
287: workflowDocumentService.disapprove(document.getDocumentHeader()
288: .getWorkflowDocument(), annotation);
289: GlobalVariables.getUserSession().setWorkflowDocument(
290: document.getDocumentHeader().getWorkflowDocument());
291: return document;
292: }
293:
294: /**
295: * @see org.kuali.core.service.DocumentService#cancelDocument(org.kuali.core.document.Document, java.lang.String)
296: */
297: public Document cancelDocument(Document document, String annotation)
298: throws WorkflowException {
299: checkForNulls(document);
300: if (!getDocumentActionFlags(document).getCanCancel()) {
301: throw buildAuthorizationException("cancel", document);
302: }
303: prepareWorkflowDocument(document);
304: workflowDocumentService.cancel(document.getDocumentHeader()
305: .getWorkflowDocument(), annotation);
306: GlobalVariables.getUserSession().setWorkflowDocument(
307: document.getDocumentHeader().getWorkflowDocument());
308: businessObjectService.delete(document.getAdHocRoutePersons());
309: businessObjectService
310: .delete(document.getAdHocRouteWorkgroups());
311: return document;
312: }
313:
314: /**
315: * @see org.kuali.core.service.DocumentService#acknowledgeDocument(org.kuali.core.document.Document, java.lang.String,
316: * java.util.List)
317: */
318: public Document acknowledgeDocument(Document document,
319: String annotation, List adHocRecipients)
320: throws WorkflowException {
321: checkForNulls(document);
322: if (!getDocumentActionFlags(document).getCanAcknowledge()) {
323: throw buildAuthorizationException("acknowledge", document);
324: }
325: prepareWorkflowDocument(document);
326: workflowDocumentService.acknowledge(document
327: .getDocumentHeader().getWorkflowDocument(), annotation,
328: adHocRecipients);
329: GlobalVariables.getUserSession().setWorkflowDocument(
330: document.getDocumentHeader().getWorkflowDocument());
331: return document;
332: }
333:
334: /**
335: * @see org.kuali.core.service.DocumentService#blanketApproveDocument(org.kuali.core.document.Document, java.lang.String,
336: * java.util.List)
337: */
338: public Document blanketApproveDocument(Document document,
339: String annotation, List adHocRecipients)
340: throws ValidationException, WorkflowException {
341: checkForNulls(document);
342: if (!getDocumentActionFlags(document).getCanBlanketApprove()) {
343: throw buildAuthorizationException("blanket approve",
344: document);
345: }
346: document.prepareForSave();
347: validateAndPersistDocument(document,
348: new BlanketApproveDocumentEvent(document));
349: prepareWorkflowDocument(document);
350: workflowDocumentService.blanketApprove(document
351: .getDocumentHeader().getWorkflowDocument(), annotation,
352: adHocRecipients);
353: GlobalVariables.getUserSession().setWorkflowDocument(
354: document.getDocumentHeader().getWorkflowDocument());
355: return document;
356: }
357:
358: /**
359: * @see org.kuali.core.service.DocumentService#clearDocumentFyi(org.kuali.core.document.Document, java.util.List)
360: */
361: public Document clearDocumentFyi(Document document,
362: List adHocRecipients) throws WorkflowException {
363: checkForNulls(document);
364: if (!getDocumentActionFlags(document).getCanFYI()) {
365: throw buildAuthorizationException("clear FYI", document);
366: }
367: // populate document content so searchable attributes will be indexed properly
368: document.populateDocumentForRouting();
369: workflowDocumentService.clearFyi(document.getDocumentHeader()
370: .getWorkflowDocument(), adHocRecipients);
371: GlobalVariables.getUserSession().setWorkflowDocument(
372: document.getDocumentHeader().getWorkflowDocument());
373: return document;
374: }
375:
376: protected void checkForNulls(Document document) {
377: if (document == null) {
378: throw new IllegalArgumentException(
379: "invalid (null) document");
380: } else if (document.getDocumentNumber() == null) {
381: throw new IllegalStateException(
382: "invalid (null) documentHeaderId");
383: }
384: }
385:
386: private DocumentActionFlags getDocumentActionFlags(Document document) {
387: UniversalUser currentUser = GlobalVariables.getUserSession()
388: .getUniversalUser();
389:
390: return documentAuthorizationService.getDocumentAuthorizer(
391: document).getDocumentActionFlags(document, currentUser);
392: }
393:
394: private DocumentAuthorizationException buildAuthorizationException(
395: String action, Document document) {
396: UniversalUser currentUser = GlobalVariables.getUserSession()
397: .getUniversalUser();
398:
399: return new DocumentAuthorizationException(currentUser
400: .getPersonUserIdentifier(), action, document
401: .getDocumentNumber());
402: }
403:
404: private void validateAndPersistDocumentAndSaveAdHocRoutingRecipients(
405: Document document, KualiDocumentEvent event)
406: throws WorkflowException {
407: /*
408: * Using this method to wrap validateAndPersistDocument to keep everything in one transaction. This avoids modifying the
409: * signature on validateAndPersistDocument method
410: */
411: ArrayList<AdHocRouteRecipient> adHocRoutingRecipients = new ArrayList();
412: adHocRoutingRecipients.addAll(document.getAdHocRoutePersons());
413: adHocRoutingRecipients.addAll(document
414: .getAdHocRouteWorkgroups());
415:
416: for (AdHocRouteRecipient recipient : adHocRoutingRecipients)
417: recipient.setdocumentNumber(document.getDocumentNumber());
418: HashMap criteria = new HashMap();
419: criteria.put("documentNumber", document.getDocumentNumber());
420: businessObjectService.deleteMatching(AdHocRouteRecipient.class,
421: criteria);
422:
423: businessObjectService.save(adHocRoutingRecipients);
424: validateAndPersistDocument(document, event);
425: }
426:
427: /**
428: * @see org.kuali.core.service.DocumentService#documentExists(java.lang.String)
429: */
430: public boolean documentExists(String documentHeaderId) {
431: boolean exists = false;
432:
433: // validate parameters
434: if (StringUtils.isBlank(documentHeaderId)) {
435: throw new IllegalArgumentException(
436: "invalid (blank) documentHeaderId");
437: }
438: if (GlobalVariables.getUserSession() == null) {
439: throw new IllegalStateException(
440: "GlobalVariables must be populated with a valid UserSession before a document can be fetched");
441: }
442:
443: // look for workflowDocumentHeader, since that supposedly won't break the transaction
444: if (!workflowDocumentService
445: .workflowDocumentExists(documentHeaderId)) {
446: exists = false;
447: } else {
448: // look for docHeaderId, since that fails without breaking the transaction
449: return documentHeaderDao
450: .getByDocumentHeaderId(documentHeaderId) != null;
451: }
452:
453: return exists;
454: }
455:
456: /**
457: * Creates a new document by class.
458: *
459: * @see org.kuali.core.service.DocumentService#getNewDocument(java.lang.Class)
460: */
461: public Document getNewDocument(Class documentClass)
462: throws WorkflowException {
463: if (documentClass == null) {
464: throw new IllegalArgumentException(
465: "invalid (null) documentClass");
466: }
467:
468: String documentTypeName = documentTypeService
469: .getDocumentTypeNameByClass(documentClass);
470: return getNewDocument(documentTypeName);
471: }
472:
473: /**
474: * Creates a new document by document type name.
475: *
476: * @see org.kuali.core.service.DocumentService#getNewDocument(java.lang.String)
477: */
478: public Document getNewDocument(String documentTypeName)
479: throws WorkflowException {
480:
481: // argument validation
482: Timer t0 = new Timer("DocumentServiceImpl.getNewDocument");
483: if (StringUtils.isBlank(documentTypeName)) {
484: throw new IllegalArgumentException(
485: "invalid (blank) documentTypeName");
486: }
487: if (GlobalVariables.getUserSession() == null) {
488: throw new IllegalStateException(
489: "GlobalVariables must be populated with a valid UserSession before a new document can be created");
490: }
491:
492: // get the class for this docTypeName
493: Class documentClass = documentTypeService
494: .getClassByName(documentTypeName);
495: if (documentClass == null) {
496: throw new UnknownDocumentTypeException(
497: "unknown document type '" + documentTypeName + "'");
498: }
499:
500: // get the current user
501: UniversalUser currentUser = GlobalVariables.getUserSession()
502: .getUniversalUser();
503:
504: // document must be maint doc or finanancial doc
505: if (!documentTypeService
506: .getDocumentTypeByName(documentTypeName)
507: .isFinDocumentTypeActiveIndicator()) {
508: throw new InactiveDocumentTypeAuthorizationException(
509: "initiate", documentTypeName);
510: }
511:
512: // get the authorization
513: DocumentAuthorizer documentAuthorizer = documentAuthorizationService
514: .getDocumentAuthorizer(documentTypeName);
515:
516: // make sure this person is authorized to initiate
517: LOG.debug("calling canInitiate from getNewDocument()");
518: documentAuthorizer.canInitiate(documentTypeName, currentUser);
519:
520: // initiate new workflow entry, get the workflow doc
521: KualiWorkflowDocument workflowDocument = workflowDocumentService
522: .createWorkflowDocument(documentTypeName,
523: GlobalVariables.getUserSession()
524: .getUniversalUser());
525: GlobalVariables.getUserSession().setWorkflowDocument(
526: workflowDocument);
527:
528: // create a new document header object
529: DocumentHeader documentHeader = new DocumentHeader();
530: documentHeader.setWorkflowDocument(workflowDocument);
531: documentHeader.setDocumentNumber(workflowDocument
532: .getRouteHeaderId().toString());
533: // status and notes are initialized correctly in the constructor
534:
535: // build Document of specified type
536: Document document = null;
537: try {
538: // all maintenance documents have same class
539: if (documentClass.equals(MaintenanceDocumentBase.class)) {
540: document = new MaintenanceDocumentBase(documentTypeName);
541: } else {
542: // non-maintenance document
543: document = (Document) documentClass.newInstance();
544: }
545: } catch (IllegalAccessException e) {
546: throw new RuntimeException(e);
547: } catch (InstantiationException e) {
548: throw new RuntimeException(e);
549: }
550:
551: document.setDocumentHeader(documentHeader);
552: document.setDocumentNumber(documentHeader.getDocumentNumber());
553:
554: t0.log();
555: return document;
556: }
557:
558: /**
559: * This is temporary until workflow 2.0 and reads from a table to get documents whose status has changed to A (approved - no
560: * outstanding approval actions requested)
561: *
562: * @param documentHeaderId
563: * @throws WorkflowException
564: * @return Document
565: */
566: public Document getByDocumentHeaderId(String documentHeaderId)
567: throws WorkflowException {
568: if (documentHeaderId == null) {
569: throw new IllegalArgumentException(
570: "invalid (null) documentHeaderId");
571: }
572: if (GlobalVariables.getUserSession() == null) {
573: throw new IllegalStateException(
574: "GlobalVariables must be populated with a valid UserSession before a document can be fetched");
575: }
576:
577: KualiWorkflowDocument workflowDocument = null;
578:
579: LOG.info("Retrieving doc id: " + documentHeaderId
580: + " from workflow service.");
581: workflowDocument = workflowDocumentService
582: .createWorkflowDocument(Long.valueOf(documentHeaderId),
583: GlobalVariables.getUserSession()
584: .getUniversalUser());
585: GlobalVariables.getUserSession().setWorkflowDocument(
586: workflowDocument);
587:
588: Class documentClass = documentTypeService
589: .getClassByName(workflowDocument.getDocumentType());
590:
591: // retrieve the Document
592: Document document = documentDao.findByDocumentHeaderId(
593: documentClass, documentHeaderId);
594: return postProcessDocument(documentHeaderId, workflowDocument,
595: document);
596: }
597:
598: /**
599: * @see org.kuali.core.service.DocumentService#findByDocumentHeaderStatusCode(java.lang.Class, java.lang.String)
600: */
601: public Collection findByDocumentHeaderStatusCode(Class clazz,
602: String statusCode) throws WorkflowException {
603: Collection foundDocuments = documentDao
604: .findByDocumentHeaderStatusCode(clazz, statusCode);
605: Collection returnDocuments = new ArrayList();
606: for (Iterator iter = foundDocuments.iterator(); iter.hasNext();) {
607: Document doc = (Document) iter.next();
608: returnDocuments.add(getByDocumentHeaderId(doc
609: .getDocumentNumber()));
610: }
611: return returnDocuments;
612: }
613:
614: /**
615: * Performs required post-processing for every document from the documentDao
616: *
617: * @param documentHeaderId
618: * @param workflowDocument
619: * @param document
620: */
621: private Document postProcessDocument(String documentHeaderId,
622: KualiWorkflowDocument workflowDocument, Document document) {
623: if (document != null) {
624: document.getDocumentHeader().setWorkflowDocument(
625: workflowDocument);
626:
627: // set correctedByDocumentId manually, since OJB doesn't maintain that relationship
628: DocumentHeader correctingDocumentHeader = documentHeaderDao
629: .getCorrectingDocumentHeader(documentHeaderId);
630: if (correctingDocumentHeader != null) {
631: document.getDocumentHeader().setCorrectedByDocumentId(
632: correctingDocumentHeader.getDocumentNumber());
633: }
634:
635: // set the ad hoc route recipients too, since OJB doesn't maintain that relationship
636: // TODO - see KULNRVSYS-1054
637:
638: document.processAfterRetrieve();
639: }
640:
641: return document;
642: }
643:
644: /**
645: * The default implementation - this retrieves all documents by a list of documentHeader for a given class.
646: *
647: * @see org.kuali.core.service.DocumentService#getDocumentsByListOfDocumentHeaderIds(java.lang.Class, java.util.List)
648: */
649: public List getDocumentsByListOfDocumentHeaderIds(Class clazz,
650: List documentHeaderIds) throws WorkflowException {
651: // validate user session
652: if (GlobalVariables.getUserSession() == null) {
653: throw new IllegalStateException(
654: "GlobalVariables must be populated with a valid UserSession before a document can be fetched");
655: }
656:
657: // make sure that the supplied class is of the document type
658: if (!Document.class.isAssignableFrom(clazz)) {
659: throw new IllegalArgumentException(
660: "invalid (non-document) class of "
661: + clazz.getName());
662: }
663:
664: // validate documentHeaderIdList and contents
665: if (documentHeaderIds == null) {
666: throw new IllegalArgumentException(
667: "invalid (null) documentHeaderId list");
668: }
669: int index = 0;
670: for (Iterator i = documentHeaderIds.iterator(); i.hasNext(); index++) {
671: String documentHeaderId = (String) i.next();
672: if (StringUtils.isBlank(documentHeaderId)) {
673: throw new IllegalArgumentException(
674: "invalid (blank) documentHeaderId at list index "
675: + index);
676: }
677: }
678:
679: // retrieve all documents that match the document header ids
680: List rawDocuments = documentDao.findByDocumentHeaderIds(clazz,
681: documentHeaderIds);
682:
683: // post-process them
684: List documents = new ArrayList();
685: for (Iterator i = rawDocuments.iterator(); i.hasNext();) {
686: Document document = (Document) i.next();
687:
688: KualiWorkflowDocument workflowDocument = workflowDocumentService
689: .createWorkflowDocument(Long.valueOf(document
690: .getDocumentNumber()), GlobalVariables
691: .getUserSession().getUniversalUser());
692:
693: document = postProcessDocument(
694: document.getDocumentNumber(), workflowDocument,
695: document);
696: documents.add(document);
697: }
698:
699: return documents;
700: }
701:
702: /**
703: * @see org.kuali.core.service.DocumentService#getFinalDocumentHeadersByDate(Date documentFinalDate)
704: */
705: public Collection getFinalDocumentHeadersByDate(
706: Date documentFinalDate) throws WorkflowException {
707: Collection finalDocumentHeaders = documentHeaderDao
708: .getByDocumentFinalDate(new java.sql.Date(
709: documentFinalDate.getTime()));
710: Iterator finalDocumentHeaderItr = finalDocumentHeaders
711: .iterator();
712: while (finalDocumentHeaderItr.hasNext()) {
713: DocumentHeader finalDocumentHeader = (DocumentHeader) finalDocumentHeaderItr
714: .next();
715: finalDocumentHeader
716: .setWorkflowDocument(workflowDocumentService
717: .createWorkflowDocument(Long
718: .valueOf(finalDocumentHeader
719: .getDocumentNumber()),
720: GlobalVariables.getUserSession()
721: .getUniversalUser()));
722: }
723: return finalDocumentHeaders;
724: }
725:
726: /* Helper Methods */
727:
728: /**
729: * Validates and persists a document.
730: *
731: * @see org.kuali.core.service.DocumentService#validateAndPersistDocument(org.kuali.core.document.Document, java.lang.String)
732: */
733: public void validateAndPersistDocument(Document document,
734: KualiDocumentEvent event) throws WorkflowException,
735: ValidationException {
736: if (document == null) {
737: LOG.error("document passed to validateAndPersist was null");
738: throw new IllegalArgumentException(
739: "invalid (null) document");
740: }
741: LOG.info("validating and preparing to persist document "
742: + document.getDocumentNumber());
743:
744: document.validateBusinessRules(event);
745: document.prepareForSave(event);
746:
747: // save the document
748: try {
749: LOG
750: .info("storing document "
751: + document.getDocumentNumber());
752: documentDao.save(document);
753: } catch (OptimisticLockingFailureException e) {
754: LOG.error("exception encountered on store of document "
755: + e.getMessage());
756: throw e;
757: }
758:
759: document.postProcessSave(event);
760:
761: }
762:
763: /**
764: * Sets the title and app document id in the flex document
765: *
766: * @param document
767: * @throws WorkflowException
768: */
769: public void prepareWorkflowDocument(Document document)
770: throws WorkflowException {
771: // populate document content so searchable attributes will be indexed properly
772: document.populateDocumentForRouting();
773:
774: // make sure we push the document title into the FlexDoc
775: populateDocumentTitle(document);
776:
777: // make sure we push the application document id into the FlexDoc
778: populateApplicationDocumentId(document);
779: }
780:
781: /**
782: * This method will grab the generated document title from the document and add it to the FlexDoc so that it gets pushed into
783: * workflow when routed.
784: *
785: * @param document
786: * @throws WorkflowException
787: */
788: private void populateDocumentTitle(Document document)
789: throws WorkflowException {
790: String documentTitle = document.getDocumentTitle();
791: if (StringUtils.isNotBlank(documentTitle)) {
792: document.getDocumentHeader().getWorkflowDocument()
793: .setTitle(documentTitle);
794: }
795: }
796:
797: /**
798: * This method will grab the organization document number from the document and add it to the FlexDoc so that it gets pushed
799: * into workflow when routed.
800: *
801: * @param document
802: */
803: private void populateApplicationDocumentId(Document document) {
804: String organizationDocumentNumber = document
805: .getDocumentHeader().getOrganizationDocumentNumber();
806: if (StringUtils.isNotBlank(organizationDocumentNumber)) {
807: document.getDocumentHeader().getWorkflowDocument()
808: .setAppDocId(organizationDocumentNumber);
809: }
810: }
811:
812: /**
813: * This is to allow for updates of document statuses and other related requirements for updates outside of the initial save and
814: * route
815: */
816: public void updateDocument(Document document) {
817: checkForNulls(document);
818: documentDao.save(document);
819: }
820:
821: /**
822: *
823: * @see org.kuali.core.service.DocumentService#createNoteFromDocument(org.kuali.core.document.Document, java.lang.String)
824: */
825: public Note createNoteFromDocument(Document document, String text)
826: throws Exception {
827: Note note = new Note();
828:
829: note.setNotePostedTimestamp(dateTimeService
830: .getCurrentTimestamp());
831: note.setVersionNumber(new Long(1));
832: note.setNoteText(text);
833: if (document.isBoNotesSupport()) {
834: note
835: .setNoteTypeCode(RiceConstants.NoteTypeEnum.BUSINESS_OBJECT_NOTE_TYPE
836: .getCode());
837: } else {
838: note
839: .setNoteTypeCode(RiceConstants.NoteTypeEnum.DOCUMENT_HEADER_NOTE_TYPE
840: .getCode());
841: }
842:
843: PersistableBusinessObject bo = null;
844: String propertyName = noteService.extractNoteProperty(note);
845: bo = (PersistableBusinessObject) ObjectUtils.getPropertyValue(
846: document, propertyName);
847: return (bo == null) ? null : noteService.createNote(note, bo);
848: }
849:
850: /**
851: * @see org.kuali.core.service.DocumentService#addNoteToDocument(org.kuali.core.document.Document, org.kuali.core.bo.Note)
852: */
853: public boolean addNoteToDocument(Document document, Note note) {
854: PersistableBusinessObject parent = getNoteParent(document, note);
855: return parent.addNote(note);
856: }
857:
858: public PersistableBusinessObject getNoteParent(Document document,
859: Note newNote) {
860: //get the property name to set (this assumes this is a document type note)
861: String propertyName = noteService.extractNoteProperty(newNote);
862: //get BO to set
863: PersistableBusinessObject noteParent = (PersistableBusinessObject) ObjectUtils
864: .getPropertyValue(document, propertyName);
865: return noteParent;
866: }
867:
868: /**
869: * @param documentTypeName
870: * @return DocumentAuthorizer instance for the given documentType name
871: */
872: private DocumentAuthorizer getDocumentAuthorizer(
873: String documentTypeName) {
874: return documentAuthorizationService
875: .getDocumentAuthorizer(documentTypeName);
876: }
877:
878: /**
879: * spring injected document type service
880: *
881: * @param documentTypeService
882: */
883: public void setDocumentTypeService(
884: DocumentTypeService documentTypeService) {
885: this .documentTypeService = documentTypeService;
886: }
887:
888: /**
889: * spring injected date time service
890: *
891: * @param dateTimeService
892: */
893: public void setDateTimeService(DateTimeService dateTimeService) {
894: this .dateTimeService = dateTimeService;
895: }
896:
897: /**
898: * @param kualiRuleService The kualiRuleService to set.
899: */
900: public void setKualiRuleService(KualiRuleService kualiRuleService) {
901: this .kualiRuleService = kualiRuleService;
902: }
903:
904: /**
905: * @param dictionaryValidationService The dictionaryValidationService to set.
906: */
907: public void setDictionaryValidationService(
908: DictionaryValidationService dictionaryValidationService) {
909: this .dictionaryValidationService = dictionaryValidationService;
910: }
911:
912: /**
913: * dao injected by spring
914: *
915: * @param documentHeaderDao
916: */
917: public void setDocumentHeaderDao(DocumentHeaderDao documentHeaderDao) {
918: this .documentHeaderDao = documentHeaderDao;
919: }
920:
921: /**
922: * Sets the maintenanceDocumentService attribute value.
923: *
924: * @param maintenanceDocumentService The maintenanceDocumentService to set.
925: */
926: public final void setMaintenanceDocumentService(
927: MaintenanceDocumentService maintenanceDocumentService) {
928: this .maintenanceDocumentService = maintenanceDocumentService;
929: }
930:
931: /**
932: * Sets the noteService attribute value.
933: * @param noteService The noteService to set.
934: */
935: public void setNoteService(NoteService noteService) {
936: this .noteService = noteService;
937: }
938:
939: /**
940: * Sets the businessObjectService attribute value.
941: *
942: * @param businessObjectService The businessObjectService to set.
943: */
944: public void setBusinessObjectService(
945: BusinessObjectService businessObjectService) {
946: this .businessObjectService = businessObjectService;
947: }
948:
949: /**
950: * Sets the workflowDocumentService attribute value.
951: *
952: * @param workflowDocumentService The workflowDocumentService to set.
953: */
954: public void setWorkflowDocumentService(
955: WorkflowDocumentService workflowDocumentService) {
956: this .workflowDocumentService = workflowDocumentService;
957: }
958:
959: /**
960: * Sets the documentAuthorizationService attribute value.
961: *
962: * @param documentAuthorizationService The documentAuthorizationService to set.
963: */
964: public void setDocumentAuthorizationService(
965: DocumentAuthorizationService documentAuthorizationService) {
966: this .documentAuthorizationService = documentAuthorizationService;
967: }
968:
969: /**
970: * Sets the documentDao attribute value.
971: *
972: * @param documentDao The documentDao to set.
973: */
974: public void setDocumentDao(DocumentDao documentDao) {
975: this.documentDao = documentDao;
976: }
977:
978: }
|