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