1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.rice.krad.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.lang.time.StopWatch;
20  import org.kuali.rice.core.api.CoreApiServiceLocator;
21  import org.kuali.rice.core.api.config.ConfigurationException;
22  import org.kuali.rice.core.api.config.property.ConfigurationService;
23  import org.kuali.rice.core.api.datetime.DateTimeService;
24  import org.kuali.rice.core.api.util.RiceKeyConstants;
25  import org.kuali.rice.core.framework.persistence.jta.TransactionalNoValidationExceptionRollback;
26  import org.kuali.rice.kew.api.WorkflowDocument;
27  import org.kuali.rice.kew.api.exception.WorkflowException;
28  import org.kuali.rice.kim.api.identity.Person;
29  import org.kuali.rice.kim.api.identity.PersonService;
30  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
31  import org.kuali.rice.krad.UserSession;
32  import org.kuali.rice.krad.bo.AdHocRoutePerson;
33  import org.kuali.rice.krad.bo.AdHocRouteRecipient;
34  import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
35  import org.kuali.rice.krad.bo.BusinessObject;
36  import org.kuali.rice.krad.bo.DocumentHeader;
37  import org.kuali.rice.krad.bo.Note;
38  import org.kuali.rice.krad.bo.PersistableBusinessObject;
39  import org.kuali.rice.krad.dao.DocumentDao;
40  import org.kuali.rice.krad.datadictionary.exception.UnknownDocumentTypeException;
41  import org.kuali.rice.krad.document.Document;
42  import org.kuali.rice.krad.document.MaintenanceDocument;
43  import org.kuali.rice.krad.document.MaintenanceDocumentBase;
44  import org.kuali.rice.krad.document.authorization.DocumentAuthorizer;
45  import org.kuali.rice.krad.document.authorization.DocumentPresentationController;
46  import org.kuali.rice.krad.exception.DocumentAuthorizationException;
47  import org.kuali.rice.krad.exception.ValidationException;
48  import org.kuali.rice.krad.rule.event.ApproveDocumentEvent;
49  import org.kuali.rice.krad.rule.event.BlanketApproveDocumentEvent;
50  import org.kuali.rice.krad.rule.event.KualiDocumentEvent;
51  import org.kuali.rice.krad.rule.event.RouteDocumentEvent;
52  import org.kuali.rice.krad.rule.event.SaveDocumentEvent;
53  import org.kuali.rice.krad.rule.event.SaveEvent;
54  import org.kuali.rice.krad.service.BusinessObjectService;
55  import org.kuali.rice.krad.service.DataDictionaryService;
56  import org.kuali.rice.krad.service.DocumentHeaderService;
57  import org.kuali.rice.krad.service.DocumentHelperService;
58  import org.kuali.rice.krad.service.DocumentService;
59  import org.kuali.rice.krad.service.KRADServiceLocator;
60  import org.kuali.rice.krad.service.KRADServiceLocatorInternal;
61  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
62  import org.kuali.rice.krad.service.NoteService;
63  import org.kuali.rice.krad.util.GlobalVariables;
64  import org.kuali.rice.krad.util.KRADConstants;
65  import org.kuali.rice.krad.util.ObjectUtils;
66  import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
67  import org.springframework.dao.OptimisticLockingFailureException;
68  
69  import java.lang.reflect.Constructor;
70  import java.lang.reflect.InvocationTargetException;
71  import java.text.MessageFormat;
72  import java.util.ArrayList;
73  import java.util.HashMap;
74  import java.util.List;
75  import java.util.Map;
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  @TransactionalNoValidationExceptionRollback
87  public class DocumentServiceImpl implements DocumentService {
88      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentServiceImpl.class);
89  
90      private DateTimeService dateTimeService;
91  
92      private NoteService noteService;
93  
94      protected WorkflowDocumentService workflowDocumentService;
95  
96      protected BusinessObjectService businessObjectService;
97  
98      
99  
100 
101     private DocumentDao documentDao;
102 
103     private DataDictionaryService dataDictionaryService;
104 
105     private DocumentHeaderService documentHeaderService;
106 
107     private PersonService personService;
108 
109     private DocumentHelperService documentHelperService;
110 
111     private ConfigurationService kualiConfigurationService;
112 
113     
114 
115 
116     @Override
117     public Document saveDocument(Document document) throws WorkflowException, ValidationException {
118         return saveDocument(document, SaveDocumentEvent.class);
119     }
120 
121     @Override
122     public Document saveDocument(Document document,
123             Class<? extends KualiDocumentEvent> kualiDocumentEventClass) throws WorkflowException, ValidationException {
124         checkForNulls(document);
125         if (kualiDocumentEventClass == null) {
126             throw new IllegalArgumentException("invalid (null) kualiDocumentEventClass");
127         }
128         
129         if (!SaveEvent.class.isAssignableFrom(kualiDocumentEventClass)) {
130             throw new ConfigurationException("The KualiDocumentEvent class '" + kualiDocumentEventClass.getName() +
131                     "' does not implement the class '" + SaveEvent.class.getName() + "'");
132         }
133 
134 
135 
136         document.prepareForSave();
137         Document savedDocument = validateAndPersistDocumentAndSaveAdHocRoutingRecipients(document,
138                 generateKualiDocumentEvent(document, kualiDocumentEventClass));
139         prepareWorkflowDocument(savedDocument);
140         getWorkflowDocumentService().save(savedDocument.getDocumentHeader().getWorkflowDocument(), null);
141 
142         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
143                 savedDocument.getDocumentHeader().getWorkflowDocument());
144 
145         return savedDocument;
146     }
147 
148     private KualiDocumentEvent generateKualiDocumentEvent(Document document,
149             Class<? extends KualiDocumentEvent> eventClass) throws ConfigurationException {
150         String potentialErrorMessage =
151                 "Found error trying to generate Kuali Document Event using event class '" + eventClass.getName() +
152                         "' for document " + document.getDocumentNumber();
153 
154         try {
155             Constructor<?> usableConstructor = null;
156             List<Object> paramList = new ArrayList<Object>();
157             for (Constructor<?> currentConstructor : eventClass.getConstructors()) {
158                 for (Class<?> parameterClass : currentConstructor.getParameterTypes()) {
159                     if (Document.class.isAssignableFrom(parameterClass)) {
160                         usableConstructor = currentConstructor;
161                         paramList.add(document);
162                     } else {
163                         paramList.add(null);
164                     }
165                 }
166                 if (ObjectUtils.isNotNull(usableConstructor)) {
167                     break;
168                 }
169             }
170             if (usableConstructor == null) {
171                 throw new RuntimeException("Cannot find a constructor for class '" + eventClass.getName() +
172                         "' that takes in a document parameter");
173             }
174             return (KualiDocumentEvent) usableConstructor.newInstance(paramList.toArray());
175         } catch (SecurityException e) {
176             throw new ConfigurationException(potentialErrorMessage, e);
177         } catch (IllegalArgumentException e) {
178             throw new ConfigurationException(potentialErrorMessage, e);
179         } catch (InstantiationException e) {
180             throw new ConfigurationException(potentialErrorMessage, e);
181         } catch (IllegalAccessException e) {
182             throw new ConfigurationException(potentialErrorMessage, e);
183         } catch (InvocationTargetException e) {
184             throw new ConfigurationException(potentialErrorMessage, e);
185         }
186     }
187 
188     
189 
190 
191 
192     @Override
193     public Document routeDocument(Document document, String annotation,
194             List<AdHocRouteRecipient> adHocRecipients) throws ValidationException, WorkflowException {
195         checkForNulls(document);
196         
197         
198         
199         document.prepareForSave();
200         Document savedDocument = validateAndPersistDocument(document, new RouteDocumentEvent(document));
201         prepareWorkflowDocument(savedDocument);
202         getWorkflowDocumentService()
203                 .route(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients);
204         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
205                 savedDocument.getDocumentHeader().getWorkflowDocument());
206         removeAdHocPersonsAndWorkgroups(savedDocument);
207         return savedDocument;
208     }
209 
210     
211 
212 
213 
214 
215     @Override
216     public Document approveDocument(Document document, String annotation,
217             List<AdHocRouteRecipient> adHocRecipients) throws ValidationException, WorkflowException {
218         checkForNulls(document);
219         
220         
221         
222         document.prepareForSave();
223         Document savedDocument = validateAndPersistDocument(document, new ApproveDocumentEvent(document));
224         prepareWorkflowDocument(savedDocument);
225         getWorkflowDocumentService()
226                 .approve(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients);
227         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
228                 savedDocument.getDocumentHeader().getWorkflowDocument());
229         removeAdHocPersonsAndWorkgroups(savedDocument);
230         return savedDocument;
231     }
232 
233     
234 
235 
236 
237     @Override
238     public Document superUserApproveDocument(Document document, String annotation) throws WorkflowException {
239         getDocumentDao().save(document);
240         prepareWorkflowDocument(document);
241         getWorkflowDocumentService().superUserApprove(document.getDocumentHeader().getWorkflowDocument(), annotation);
242         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
243                 document.getDocumentHeader().getWorkflowDocument());
244         removeAdHocPersonsAndWorkgroups(document);
245         return document;
246     }
247 
248     
249 
250 
251 
252     @Override
253     public Document superUserCancelDocument(Document document, String annotation) throws WorkflowException {
254         getDocumentDao().save(document);
255         prepareWorkflowDocument(document);
256         getWorkflowDocumentService().superUserCancel(document.getDocumentHeader().getWorkflowDocument(), annotation);
257         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
258                 document.getDocumentHeader().getWorkflowDocument());
259         removeAdHocPersonsAndWorkgroups(document);
260         return document;
261     }
262 
263     
264 
265 
266 
267     @Override
268     public Document superUserDisapproveDocument(Document document, String annotation) throws WorkflowException {
269         getDocumentDao().save(document);
270         prepareWorkflowDocument(document);
271         getWorkflowDocumentService()
272                 .superUserDisapprove(document.getDocumentHeader().getWorkflowDocument(), annotation);
273         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
274                 document.getDocumentHeader().getWorkflowDocument());
275         removeAdHocPersonsAndWorkgroups(document);
276         return document;
277     }
278 
279     
280 
281 
282 
283     @Override
284     public Document disapproveDocument(Document document, String annotation) throws Exception {
285         checkForNulls(document);
286 
287         Note note = createNoteFromDocument(document, annotation);
288         document.addNote(note);
289 
290         
291         
292         
293         getNoteService().save(note);
294 
295         prepareWorkflowDocument(document);
296         getWorkflowDocumentService().disapprove(document.getDocumentHeader().getWorkflowDocument(), annotation);
297         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
298                 document.getDocumentHeader().getWorkflowDocument());
299         removeAdHocPersonsAndWorkgroups(document);
300         return document;
301     }
302 
303     
304 
305 
306 
307     @Override
308     public Document cancelDocument(Document document, String annotation) throws WorkflowException {
309         checkForNulls(document);
310         
311         
312         
313         if (document instanceof MaintenanceDocument) {
314             MaintenanceDocument maintDoc = ((MaintenanceDocument) document);
315             if (maintDoc.getOldMaintainableObject() != null &&
316                     (maintDoc.getOldMaintainableObject().getDataObject() instanceof BusinessObject)) {
317                 ((BusinessObject) maintDoc.getOldMaintainableObject().getDataObject()).refresh();
318             }
319 
320             if (maintDoc.getNewMaintainableObject().getDataObject() instanceof BusinessObject) {
321                 ((BusinessObject) maintDoc.getNewMaintainableObject().getDataObject()).refresh();
322             }
323         }
324         prepareWorkflowDocument(document);
325         getWorkflowDocumentService().cancel(document.getDocumentHeader().getWorkflowDocument(), annotation);
326         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
327                 document.getDocumentHeader().getWorkflowDocument());
328         
329         
330         removeAdHocPersonsAndWorkgroups(document);
331         return document;
332     }
333 
334     
335 
336 
337 
338 
339     @Override
340     public Document acknowledgeDocument(Document document, String annotation,
341             List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
342         checkForNulls(document);
343         
344         
345         
346         prepareWorkflowDocument(document);
347         getWorkflowDocumentService()
348                 .acknowledge(document.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients);
349         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
350                 document.getDocumentHeader().getWorkflowDocument());
351         removeAdHocPersonsAndWorkgroups(document);
352         return document;
353     }
354 
355     
356 
357 
358 
359 
360     @Override
361     public Document blanketApproveDocument(Document document, String annotation,
362             List<AdHocRouteRecipient> adHocRecipients) throws ValidationException, WorkflowException {
363         checkForNulls(document);
364         
365         
366         
367         document.prepareForSave();
368         Document savedDocument = validateAndPersistDocument(document, new BlanketApproveDocumentEvent(document));
369         prepareWorkflowDocument(savedDocument);
370         getWorkflowDocumentService()
371                 .blanketApprove(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients);
372         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
373                 savedDocument.getDocumentHeader().getWorkflowDocument());
374         removeAdHocPersonsAndWorkgroups(savedDocument);
375         return savedDocument;
376     }
377 
378     
379 
380 
381 
382     @Override
383     public Document clearDocumentFyi(Document document,
384             List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
385         checkForNulls(document);
386         
387         document.populateDocumentForRouting();
388         getWorkflowDocumentService().clearFyi(document.getDocumentHeader().getWorkflowDocument(), adHocRecipients);
389         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
390                 document.getDocumentHeader().getWorkflowDocument());
391         removeAdHocPersonsAndWorkgroups(document);
392         return document;
393     }
394 
395     protected void checkForNulls(Document document) {
396         if (document == null) {
397             throw new IllegalArgumentException("invalid (null) document");
398         }
399         if (document.getDocumentNumber() == null) {
400             throw new IllegalStateException("invalid (null) documentHeaderId");
401         }
402     }
403 
404     private Document validateAndPersistDocumentAndSaveAdHocRoutingRecipients(Document document,
405             KualiDocumentEvent event) {
406         
407 
408 
409 
410         List<AdHocRouteRecipient> adHocRoutingRecipients = new ArrayList<AdHocRouteRecipient>();
411         adHocRoutingRecipients.addAll(document.getAdHocRoutePersons());
412         adHocRoutingRecipients.addAll(document.getAdHocRouteWorkgroups());
413 
414         for (AdHocRouteRecipient recipient : adHocRoutingRecipients) {
415             recipient.setdocumentNumber(document.getDocumentNumber());
416         }
417         Map<String, String> criteria = new HashMap<String, String>();
418         criteria.put("documentNumber", document.getDocumentNumber());
419         getBusinessObjectService().deleteMatching(AdHocRouteRecipient.class, criteria);
420 
421         getBusinessObjectService().save(adHocRoutingRecipients);
422         return validateAndPersistDocument(document, event);
423     }
424 
425     
426 
427 
428     @Override
429     public boolean documentExists(String documentHeaderId) {
430         
431         if (StringUtils.isBlank(documentHeaderId)) {
432             throw new IllegalArgumentException("invalid (blank) documentHeaderId");
433         }
434 
435         boolean internalUserSession = false;
436         try {
437             
438             
439             if (GlobalVariables.getUserSession() == null) {
440                 internalUserSession = true;
441                 GlobalVariables.setUserSession(new UserSession(KRADConstants.SYSTEM_USER));
442                 GlobalVariables.clear();
443             }
444 
445             
446             if (getWorkflowDocumentService().workflowDocumentExists(documentHeaderId)) {
447                 
448                 return getDocumentHeaderService().getDocumentHeaderById(documentHeaderId) != null;
449             }
450 
451             return false;
452         } finally {
453             
454             if (internalUserSession) {
455                 GlobalVariables.clear();
456                 GlobalVariables.setUserSession(null);
457             }
458         }
459     }
460 
461     
462 
463 
464 
465 
466     @Override
467     public Document getNewDocument(Class<? extends Document> documentClass) throws WorkflowException {
468         if (documentClass == null) {
469             throw new IllegalArgumentException("invalid (null) documentClass");
470         }
471         if (!Document.class.isAssignableFrom(documentClass)) {
472             throw new IllegalArgumentException("invalid (non-Document) documentClass");
473         }
474 
475         String documentTypeName = getDataDictionaryService().getDocumentTypeNameByClass(documentClass);
476         if (StringUtils.isBlank(documentTypeName)) {
477             throw new UnknownDocumentTypeException(
478                     "unable to get documentTypeName for unknown documentClass '" + documentClass.getName() + "'");
479         }
480         return getNewDocument(documentTypeName);
481     }
482 
483     
484 
485 
486 
487 
488     @Override
489     public Document getNewDocument(String documentTypeName) throws WorkflowException {
490 
491         
492         String watchName = "DocumentServiceImpl.getNewDocument";
493         StopWatch watch = new StopWatch();
494         watch.start();
495         if (LOG.isDebugEnabled()) {
496             LOG.debug(watchName + ": started");
497         }
498         if (StringUtils.isBlank(documentTypeName)) {
499             throw new IllegalArgumentException("invalid (blank) documentTypeName");
500         }
501         if (GlobalVariables.getUserSession() == null) {
502             throw new IllegalStateException(
503                     "GlobalVariables must be populated with a valid UserSession before a new document can be created");
504         }
505 
506         
507         Class<? extends Document> documentClass = getDocumentClassByTypeName(documentTypeName);
508 
509         
510         Person currentUser = GlobalVariables.getUserSession().getPerson();
511 
512         
513         DocumentAuthorizer documentAuthorizer = getDocumentHelperService().getDocumentAuthorizer(documentTypeName);
514         DocumentPresentationController documentPresentationController =
515                 getDocumentHelperService().getDocumentPresentationController(documentTypeName);
516         
517         LOG.debug("calling canInitiate from getNewDocument()");
518         if (!documentPresentationController.canInitiate(documentTypeName) ||
519                 !documentAuthorizer.canInitiate(documentTypeName, currentUser)) {
520             throw new DocumentAuthorizationException(currentUser.getPrincipalName(), "initiate", documentTypeName);
521         }
522 
523         
524         WorkflowDocument workflowDocument = getWorkflowDocumentService().createWorkflowDocument(documentTypeName, GlobalVariables.getUserSession().getPerson());
525         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),workflowDocument);
526 
527         
528         DocumentHeader documentHeader = null;
529         try {
530             
531             Class<? extends DocumentHeader> documentHeaderClass =
532                     getDocumentHeaderService().getDocumentHeaderBaseClass();
533             documentHeader = documentHeaderClass.newInstance();
534             documentHeader.setWorkflowDocument(workflowDocument);
535             documentHeader.setDocumentNumber(workflowDocument.getDocumentId());
536             
537         } catch (IllegalAccessException e) {
538             throw new RuntimeException("Error instantiating DocumentHeader", e);
539         } catch (InstantiationException e) {
540             throw new RuntimeException("Error instantiating DocumentHeader", e);
541         }
542 
543         
544         Document document = null;
545         try {
546             
547             if (MaintenanceDocumentBase.class.isAssignableFrom(documentClass)) {
548                 Class<?>[] defaultConstructor = new Class[]{String.class};
549                 Constructor<? extends Document> cons = documentClass.getConstructor(defaultConstructor);
550                 if (ObjectUtils.isNull(cons)) {
551                     throw new ConfigurationException(
552                             "Could not find constructor with document type name parameter needed for Maintenance Document Base class");
553                 }
554                 document = cons.newInstance(documentTypeName);
555             } else {
556                 
557                 document = documentClass.newInstance();
558             }
559         } catch (IllegalAccessException e) {
560             throw new RuntimeException("Error instantiating Document", e);
561         } catch (InstantiationException e) {
562             throw new RuntimeException("Error instantiating Document", e);
563         } catch (SecurityException e) {
564             throw new RuntimeException("Error instantiating Maintenance Document", e);
565         } catch (NoSuchMethodException e) {
566             throw new RuntimeException(
567                     "Error instantiating Maintenance Document: No constructor with String parameter found", e);
568         } catch (IllegalArgumentException e) {
569             throw new RuntimeException("Error instantiating Maintenance Document", e);
570         } catch (InvocationTargetException e) {
571             throw new RuntimeException("Error instantiating Maintenance Document", e);
572         }
573 
574         document.setDocumentHeader(documentHeader);
575         document.setDocumentNumber(documentHeader.getDocumentNumber());
576 
577         watch.stop();
578         if (LOG.isDebugEnabled()) {
579             LOG.debug(watchName + ": " + watch.toString());
580         }
581         return document;
582     }
583 
584     
585 
586 
587 
588 
589 
590 
591 
592 
593     @Override
594     public Document getByDocumentHeaderId(String documentHeaderId) throws WorkflowException {
595         if (documentHeaderId == null) {
596             throw new IllegalArgumentException("invalid (null) documentHeaderId");
597         }
598         boolean internalUserSession = false;
599         try {
600             
601             
602             if (GlobalVariables.getUserSession() == null) {
603                 internalUserSession = true;
604                 GlobalVariables.setUserSession(new UserSession(KRADConstants.SYSTEM_USER));
605                 GlobalVariables.clear();
606             }
607 
608 	        WorkflowDocument workflowDocument = null;
609 
610             if (LOG.isDebugEnabled()) {
611                 LOG.debug("Retrieving doc id: " + documentHeaderId + " from workflow service.");
612             }
613             workflowDocument = getWorkflowDocumentService()
614                     .loadWorkflowDocument(documentHeaderId, GlobalVariables.getUserSession().getPerson());
615             KRADServiceLocatorWeb.getSessionDocumentService()
616                     .addDocumentToUserSession(GlobalVariables.getUserSession(), workflowDocument);
617 
618 	        Class<? extends Document> documentClass = getDocumentClassByTypeName(workflowDocument.getDocumentTypeName());
619 
620             
621             Document document = getDocumentDao().findByDocumentHeaderId(documentClass, documentHeaderId);
622 
623             return postProcessDocument(documentHeaderId, workflowDocument, document);
624         } finally {
625             
626             if (internalUserSession) {
627                 GlobalVariables.clear();
628                 GlobalVariables.setUserSession(null);
629             }
630         }
631     }
632 
633     
634 
635 
636     @Override
637     public Document getByDocumentHeaderIdSessionless(String documentHeaderId) throws WorkflowException {
638         if (documentHeaderId == null) {
639             throw new IllegalArgumentException("invalid (null) documentHeaderId");
640         }
641 
642         WorkflowDocument workflowDocument = null;
643 
644         if (LOG.isDebugEnabled()) {
645             LOG.debug("Retrieving doc id: " + documentHeaderId + " from workflow service.");
646         }
647 
648         Person person = getPersonService().getPersonByPrincipalName(KRADConstants.SYSTEM_USER);
649         workflowDocument = workflowDocumentService.loadWorkflowDocument(documentHeaderId, person);
650 
651         Class<? extends Document> documentClass = getDocumentClassByTypeName(workflowDocument.getDocumentTypeName());
652 
653         
654         Document document = getDocumentDao().findByDocumentHeaderId(documentClass, documentHeaderId);
655 
656         return postProcessDocument(documentHeaderId, workflowDocument, document);
657     }
658 
659     private Class<? extends Document> getDocumentClassByTypeName(String documentTypeName) {
660         if (StringUtils.isBlank(documentTypeName)) {
661             throw new IllegalArgumentException("invalid (blank) documentTypeName");
662         }
663 
664         Class<? extends Document> clazz = getDataDictionaryService().getDocumentClassByTypeName(documentTypeName);
665         if (clazz == null) {
666             throw new UnknownDocumentTypeException(
667                     "unable to get class for unknown documentTypeName '" + documentTypeName + "'");
668         }
669         return clazz;
670     }
671 
672     
673 
674 
675 
676 
677     protected void loadNotes(Document document) {
678         if (isNoteTargetReady(document)) {
679             List<Note> notes = getNoteService().getByRemoteObjectId(document.getNoteTarget().getObjectId());
680             
681             
682             for (Note note : notes) {
683                 note.refreshReferenceObject("attachment");
684             }
685             document.setNotes(notes);
686         }
687     }
688 
689     
690 
691 
692 
693 
694 
695 
696     private Document postProcessDocument(String documentHeaderId, WorkflowDocument workflowDocument, Document document) {
697         if (document != null) {
698             document.getDocumentHeader().setWorkflowDocument(workflowDocument);
699             document.processAfterRetrieve();
700             loadNotes(document);
701         }
702         return document;
703     }
704 
705     
706 
707 
708 
709 
710 
711     @Override
712     public List<Document> getDocumentsByListOfDocumentHeaderIds(Class<? extends Document> documentClass,
713             List<String> documentHeaderIds) throws WorkflowException {
714         
715         if (documentHeaderIds == null) {
716             throw new IllegalArgumentException("invalid (null) documentHeaderId list");
717         }
718         int index = 0;
719         for (String documentHeaderId : documentHeaderIds) {
720             if (StringUtils.isBlank(documentHeaderId)) {
721                 throw new IllegalArgumentException("invalid (blank) documentHeaderId at list index " + index);
722             }
723             index++;
724         }
725 
726         boolean internalUserSession = false;
727         try {
728             
729             
730             if (GlobalVariables.getUserSession() == null) {
731                 internalUserSession = true;
732                 GlobalVariables.setUserSession(new UserSession(KRADConstants.SYSTEM_USER));
733                 GlobalVariables.clear();
734             }
735 
736             
737             List<? extends Document> rawDocuments =
738                     getDocumentDao().findByDocumentHeaderIds(documentClass, documentHeaderIds);
739 
740 	        
741 	        List<Document> documents = new ArrayList<Document>();
742 	        for (Document document : rawDocuments) {
743 	            WorkflowDocument workflowDocument = getWorkflowDocumentService().loadWorkflowDocument(document.getDocumentNumber(), GlobalVariables.getUserSession().getPerson());
744 
745                 document = postProcessDocument(document.getDocumentNumber(), workflowDocument, document);
746                 documents.add(document);
747             }
748             return documents;
749         } finally {
750             
751             if (internalUserSession) {
752                 GlobalVariables.clear();
753                 GlobalVariables.setUserSession(null);
754             }
755         }
756     }
757 
758     
759 
760     
761 
762 
763 
764 
765 
766     public Document validateAndPersistDocument(Document document, KualiDocumentEvent event) throws ValidationException {
767         if (document == null) {
768             LOG.error("document passed to validateAndPersist was null");
769             throw new IllegalArgumentException("invalid (null) document");
770         }
771         if (LOG.isDebugEnabled()) {
772             LOG.debug("validating and preparing to persist document " + document.getDocumentNumber());
773         }
774 
775         document.validateBusinessRules(event);
776         document.prepareForSave(event);
777 
778         
779         Document savedDocument = null;
780         try {
781             if (LOG.isInfoEnabled()) {
782                 LOG.info("storing document " + document.getDocumentNumber());
783             }
784             savedDocument = getDocumentDao().save(document);
785         } catch (OptimisticLockingFailureException e) {
786             LOG.error("exception encountered on store of document " + e.getMessage());
787             throw e;
788         }
789 
790         boolean notesSaved = saveDocumentNotes(document);
791         if (!notesSaved) {
792             if (LOG.isInfoEnabled()) {
793                 LOG.info(
794                         "Notes not saved during validateAndPersistDocument, likely means that note save needs to be deferred because note target is not ready.");
795             }
796         }
797 
798         savedDocument.postProcessSave(event);
799 
800         return savedDocument;
801     }
802 
803     
804 
805 
806 
807 
808 
809     @Override
810     public void prepareWorkflowDocument(Document document) throws WorkflowException {
811         
812         document.populateDocumentForRouting();
813 
814         
815         populateDocumentTitle(document);
816 
817         
818         populateApplicationDocumentId(document);
819     }
820 
821     
822 
823 
824 
825 
826 
827 
828 
829     private void populateDocumentTitle(Document document) throws WorkflowException {
830         String documentTitle = document.getDocumentTitle();
831         if (StringUtils.isNotBlank(documentTitle)) {
832             document.getDocumentHeader().getWorkflowDocument().setTitle(documentTitle);
833         }
834     }
835 
836     
837 
838 
839 
840 
841 
842 
843     private void populateApplicationDocumentId(Document document) {
844         String organizationDocumentNumber = document.getDocumentHeader().getOrganizationDocumentNumber();
845         if (StringUtils.isNotBlank(organizationDocumentNumber)) {
846             document.getDocumentHeader().getWorkflowDocument().setApplicationDocumentId(organizationDocumentNumber);
847         }
848     }
849 
850     
851 
852 
853 
854 
855     @Override
856     public Document updateDocument(Document document) {
857         checkForNulls(document);
858         return getDocumentDao().save(document);
859     }
860 
861     
862 
863 
864 
865     @Override
866     public Note createNoteFromDocument(Document document, String text) {
867         Note note = new Note();
868 
869         note.setNotePostedTimestamp(getDateTimeService().getCurrentTimestamp());
870         note.setVersionNumber(Long.valueOf(1));
871         note.setNoteText(text);
872         note.setNoteTypeCode(document.getNoteType().getCode());
873 
874         PersistableBusinessObject bo = document.getNoteTarget();
875         
876         Person kualiUser = GlobalVariables.getUserSession().getPerson();
877         if (kualiUser == null) {
878             throw new IllegalStateException("Current UserSession has a null Person.");
879         }
880         return bo == null ? null : getNoteService().createNote(note, bo, kualiUser.getPrincipalId());
881     }
882 
883     
884 
885 
886     @Override
887     public boolean saveDocumentNotes(Document document) {
888         if (isNoteTargetReady(document)) {
889             List<Note> notes = document.getNotes();
890             for (Note note : document.getNotes()) {
891                 linkNoteRemoteObjectId(note, document.getNoteTarget());
892             }
893             getNoteService().saveNoteList(notes);
894             return true;
895         }
896         return false;
897     }
898 
899     
900 
901 
902 
903 
904 
905 
906 
907     protected boolean isNoteTargetReady(Document document) {
908         PersistableBusinessObject noteTarget = document.getNoteTarget();
909         if (noteTarget == null || StringUtils.isBlank(noteTarget.getObjectId())) {
910             return false;
911         }
912         return true;
913     }
914 
915     private void linkNoteRemoteObjectId(Note note, PersistableBusinessObject noteTarget) {
916         String objectId = noteTarget.getObjectId();
917         if (StringUtils.isBlank(objectId)) {
918             throw new IllegalStateException(
919                     "Attempted to link a Note with a PersistableBusinessObject with no object id");
920         }
921         note.setRemoteObjectIdentifier(noteTarget.getObjectId());
922     }
923 
924     
925 
926 
927 
928 
929 
930     @Override
931     public void sendAdHocRequests(Document document, String annotation,
932             List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
933         prepareWorkflowDocument(document);
934         getWorkflowDocumentService()
935                 .sendWorkflowNotification(document.getDocumentHeader().getWorkflowDocument(), annotation,
936                         adHocRecipients);
937         KRADServiceLocatorWeb.getSessionDocumentService().addDocumentToUserSession(GlobalVariables.getUserSession(),
938                 document.getDocumentHeader().getWorkflowDocument());
939         
940         
941         removeAdHocPersonsAndWorkgroups(document);
942     }
943 
944     
945 
946 
947 
948 
949     public synchronized void setDateTimeService(DateTimeService dateTimeService) {
950         this.dateTimeService = dateTimeService;
951     }
952 
953     
954 
955 
956 
957 
958     private synchronized DateTimeService getDateTimeService() {
959         if (this.dateTimeService == null) {
960             this.dateTimeService = CoreApiServiceLocator.getDateTimeService();
961         }
962         return this.dateTimeService;
963     }
964 
965     
966 
967 
968 
969 
970     public synchronized void setNoteService(NoteService noteService) {
971         this.noteService = noteService;
972     }
973 
974     
975 
976 
977 
978 
979     protected synchronized NoteService getNoteService() {
980         if (this.noteService == null) {
981             this.noteService = KRADServiceLocator.getNoteService();
982         }
983         return this.noteService;
984     }
985 
986     
987 
988 
989 
990 
991     public synchronized void setBusinessObjectService(BusinessObjectService businessObjectService) {
992         this.businessObjectService = businessObjectService;
993     }
994 
995     
996 
997 
998 
999 
1000     protected synchronized BusinessObjectService getBusinessObjectService() {
1001         if (this.businessObjectService == null) {
1002             this.businessObjectService = KRADServiceLocator.getBusinessObjectService();
1003         }
1004         return this.businessObjectService;
1005     }
1006 
1007     
1008 
1009 
1010 
1011 
1012     public synchronized void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
1013         this.workflowDocumentService = workflowDocumentService;
1014     }
1015 
1016     
1017 
1018 
1019 
1020 
1021     protected synchronized WorkflowDocumentService getWorkflowDocumentService() {
1022         if (this.workflowDocumentService == null) {
1023             this.workflowDocumentService = KRADServiceLocatorWeb.getWorkflowDocumentService();
1024         }
1025         return this.workflowDocumentService;
1026     }
1027 
1028     
1029 
1030 
1031 
1032 
1033     public synchronized void setDocumentDao(DocumentDao documentDao) {
1034         this.documentDao = documentDao;
1035     }
1036 
1037     
1038 
1039 
1040 
1041 
1042     protected synchronized DocumentDao getDocumentDao() {
1043         if (this.documentDao == null) {
1044             this.documentDao = KRADServiceLocatorInternal.getDocumentDao();
1045         }
1046         return documentDao;
1047     }
1048 
1049     
1050 
1051 
1052 
1053 
1054     public synchronized void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
1055         this.dataDictionaryService = dataDictionaryService;
1056     }
1057 
1058     
1059 
1060 
1061 
1062 
1063     protected synchronized DataDictionaryService getDataDictionaryService() {
1064         if (this.dataDictionaryService == null) {
1065             this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
1066         }
1067         return this.dataDictionaryService;
1068     }
1069 
1070     
1071 
1072 
1073     public synchronized void setDocumentHeaderService(DocumentHeaderService documentHeaderService) {
1074         this.documentHeaderService = documentHeaderService;
1075     }
1076 
1077     
1078 
1079 
1080 
1081 
1082     protected synchronized DocumentHeaderService getDocumentHeaderService() {
1083         if (this.documentHeaderService == null) {
1084             this.documentHeaderService = KRADServiceLocatorWeb.getDocumentHeaderService();
1085         }
1086         return this.documentHeaderService;
1087     }
1088 
1089     
1090 
1091 
1092     public PersonService getPersonService() {
1093         if (personService == null) {
1094             personService = KimApiServiceLocator.getPersonService();
1095         }
1096         return personService;
1097     }
1098 
1099     
1100 
1101 
1102     public DocumentHelperService getDocumentHelperService() {
1103         if (documentHelperService == null) {
1104             this.documentHelperService = KRADServiceLocatorWeb.getDocumentHelperService();
1105         }
1106         return this.documentHelperService;
1107     }
1108 
1109     
1110 
1111 
1112     public void setDocumentHelperService(DocumentHelperService documentHelperService) {
1113         this.documentHelperService = documentHelperService;
1114     }
1115 
1116     private void removeAdHocPersonsAndWorkgroups(Document document) {
1117         List<AdHocRoutePerson> adHocRoutePersons = new ArrayList<AdHocRoutePerson>();
1118         List<AdHocRouteWorkgroup> adHocRouteWorkgroups = new ArrayList<AdHocRouteWorkgroup>();
1119         getBusinessObjectService().delete(document.getAdHocRoutePersons());
1120         getBusinessObjectService().delete(document.getAdHocRouteWorkgroups());
1121         document.setAdHocRoutePersons(adHocRoutePersons);
1122         document.setAdHocRouteWorkgroups(adHocRouteWorkgroups);
1123     }
1124 
1125     
1126 
1127 
1128     @Override
1129     public void sendNoteRouteNotification(Document document, Note note, Person sender) throws WorkflowException {
1130         AdHocRouteRecipient routeRecipient = note.getAdHocRouteRecipient();
1131 
1132         
1133         Person requestedUser = this.getPersonService().getPersonByPrincipalName(routeRecipient.getId());
1134         String senderName = sender.getFirstName() + " " + sender.getLastName();
1135         String requestedName = requestedUser.getFirstName() + " " + requestedUser.getLastName();
1136 
1137         String notificationText =
1138                 kualiConfigurationService.getPropertyValueAsString(
1139                         RiceKeyConstants.MESSAGE_NOTE_NOTIFICATION_ANNOTATION);
1140         if (StringUtils.isBlank(notificationText)) {
1141             throw new RuntimeException(
1142                     "No annotation message found for note notification. Message needs added to application resources with key:" +
1143                             RiceKeyConstants.MESSAGE_NOTE_NOTIFICATION_ANNOTATION);
1144         }
1145         notificationText =
1146                 MessageFormat.format(notificationText, new Object[]{senderName, requestedName, note.getNoteText()});
1147 
1148         List<AdHocRouteRecipient> routeRecipients = new ArrayList<AdHocRouteRecipient>();
1149         routeRecipients.add(routeRecipient);
1150 
1151         workflowDocumentService
1152                 .sendWorkflowNotification(document.getDocumentHeader().getWorkflowDocument(), notificationText,
1153                         routeRecipients, KRADConstants.NOTE_WORKFLOW_NOTIFICATION_REQUEST_LABEL);
1154 
1155         
1156         note.setAdHocRouteRecipient(new AdHocRoutePerson());
1157     }
1158 
1159     
1160 
1161 
1162     public void setKualiConfigurationService(ConfigurationService kualiConfigurationService) {
1163         this.kualiConfigurationService = kualiConfigurationService;
1164     }
1165 }