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