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