View Javadoc
1   package org.kuali.rice.krad.document;
2   
3   import java.util.ArrayList;
4   import java.util.Iterator;
5   import java.util.List;
6   import java.util.Map;
7   
8   import javax.persistence.Column;
9   import javax.persistence.Id;
10  import javax.persistence.MappedSuperclass;
11  import javax.persistence.PostLoad;
12  import javax.persistence.PostRemove;
13  import javax.persistence.PrePersist;
14  import javax.persistence.Transient;
15  
16  import org.apache.commons.lang.StringUtils;
17  import org.apache.log4j.Logger;
18  import org.kuali.rice.core.api.mo.common.GloballyUnique;
19  import org.kuali.rice.kew.api.KewApiConstants;
20  import org.kuali.rice.kew.api.KewApiServiceLocator;
21  import org.kuali.rice.kew.api.WorkflowDocument;
22  import org.kuali.rice.kew.api.action.ActionRequest;
23  import org.kuali.rice.kew.api.action.ActionType;
24  import org.kuali.rice.kew.api.exception.WorkflowException;
25  import org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent;
26  import org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange;
27  import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
28  import org.kuali.rice.kim.api.identity.Person;
29  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
30  import org.kuali.rice.krad.UserSessionUtils;
31  import org.kuali.rice.krad.bo.AdHocRoutePerson;
32  import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
33  import org.kuali.rice.krad.bo.DocumentHeader;
34  import org.kuali.rice.krad.bo.Note;
35  import org.kuali.rice.krad.bo.PersistableBusinessObjectBaseAdapter;
36  import org.kuali.rice.krad.datadictionary.DocumentEntry;
37  import org.kuali.rice.krad.datadictionary.WorkflowAttributes;
38  import org.kuali.rice.krad.datadictionary.WorkflowProperties;
39  import org.kuali.rice.krad.document.authorization.PessimisticLock;
40  import org.kuali.rice.krad.exception.PessimisticLockingException;
41  import org.kuali.rice.krad.exception.ValidationException;
42  import org.kuali.rice.krad.rules.rule.event.DocumentEvent;
43  import org.kuali.rice.krad.service.AttachmentService;
44  import org.kuali.rice.krad.service.DocumentSerializerService;
45  import org.kuali.rice.krad.service.KRADServiceLocator;
46  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
47  import org.kuali.rice.krad.service.NoteService;
48  import org.kuali.rice.krad.util.ErrorMessage;
49  import org.kuali.rice.krad.util.GlobalVariables;
50  import org.kuali.rice.krad.util.KRADConstants;
51  import org.kuali.rice.krad.util.KRADPropertyConstants;
52  import org.kuali.rice.krad.util.NoteType;
53  import org.kuali.rice.krad.util.documentserializer.AlwaysFalsePropertySerializabilityEvaluator;
54  import org.kuali.rice.krad.util.documentserializer.AlwaysTruePropertySerializibilityEvaluator;
55  import org.kuali.rice.krad.util.documentserializer.BusinessObjectPropertySerializibilityEvaluator;
56  import org.kuali.rice.krad.util.documentserializer.PropertySerializabilityEvaluator;
57  import org.kuali.rice.krad.workflow.DocumentInitiator;
58  import org.kuali.rice.krad.workflow.KualiDocumentXmlMaterializer;
59  import org.kuali.rice.krad.workflow.KualiTransactionalDocumentInformation;
60  import org.springframework.util.CollectionUtils;
61  
62  /**
63   * Created by sheiksalahudeenm on 9/23/14.
64   *
65   * Overridden for fixing the 'workflow document is null' issue in KRAD Transaction document with JPA.
66   * Modified method name: prePersist.
67   * Changes description : Removed the code for saving document header. Because document header will be stored by document service.
68   */
69  @MappedSuperclass
70  public abstract class DocumentBase extends PersistableBusinessObjectBaseAdapter implements Document {
71      private static final long serialVersionUID = 8530945307802647664L;
72      private static final Logger LOG = Logger.getLogger(DocumentBase.class);
73  
74      @Id
75      @Column(name = "DOC_HDR_ID",length=14)
76      protected String documentNumber;
77  
78      @Transient
79      protected DocumentHeader documentHeader;
80  
81      @Transient
82      protected List<PessimisticLock> pessimisticLocks;
83  
84      @Transient
85      protected List<AdHocRoutePerson> adHocRoutePersons;
86      @Transient
87      protected List<AdHocRouteWorkgroup> adHocRouteWorkgroups;
88      @Transient
89      protected List<Note> notes;
90      @Transient
91      private String superUserAnnotation = "";
92  
93      private transient NoteService noteService;
94      private transient AttachmentService attachmentService;
95  
96      /**
97       * Constructs a DocumentBase.java.
98       */
99      public DocumentBase() {
100         documentHeader = new DocumentHeader();
101         pessimisticLocks = new ArrayList<PessimisticLock>();
102         adHocRoutePersons = new ArrayList<AdHocRoutePerson>();
103         adHocRouteWorkgroups = new ArrayList<AdHocRouteWorkgroup>();
104         notes = new ArrayList<Note>();
105     }
106 
107     /**
108      * @see org.kuali.rice.krad.document.Document#getAllowsCopy()
109      */
110     @Override
111     public boolean getAllowsCopy() {
112         return false;
113     }
114 
115     /**
116      * Retrieves the title of the document
117      *
118      * <p>
119      * This is the default document title implementation. It concatenates the document's data dictionary file label
120      * attribute and
121      * the document's document header description together. This title is used to populate workflow and will show up in
122      * document
123      * search results and user action lists.
124      * </p>
125      *
126      * return String representing the title of the document
127      *
128      * @see org.kuali.rice.krad.document.Document#getDocumentTitle()
129      */
130     @Override
131     public String getDocumentTitle() {
132         String documentTypeLabel = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(
133                 this.getDocumentHeader().getWorkflowDocument().getDocumentTypeName()).getLabel();
134         if (null == documentTypeLabel) {
135             documentTypeLabel = "";
136         }
137 
138         String description = this.getDocumentHeader().getDocumentDescription();
139         if (null == description) {
140             description = "";
141         }
142 
143         return documentTypeLabel + " - " + description;
144     }
145 
146     /**
147      * @see org.kuali.rice.krad.document.Document#prepareForSave()
148      */
149     @Override
150     public void prepareForSave() {
151         // do nothing
152     }
153 
154     /**
155      * @see org.kuali.rice.krad.document.Document#processAfterRetrieve()
156      */
157     @Override
158     public void processAfterRetrieve() {
159         // do nothing
160     }
161 
162     /**
163      * The the default implementation for RouteLevelChange does nothing, but is meant to provide a hook for documents to
164      * implement
165      * for other needs.
166      *
167      * @see org.kuali.rice.krad.document.Document#doRouteLevelChange(org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange)
168      */
169     @Override
170     public void doRouteLevelChange(DocumentRouteLevelChange levelChangeEvent) {
171         // do nothing
172     }
173 
174     /**
175      * @see org.kuali.rice.krad.document.Document#doActionTaken(org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent)
176      */
177     @Override
178     public void doActionTaken(ActionTakenEvent event) {
179         if ((KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(
180                 this.getClass().getName()).getUseWorkflowPessimisticLocking()) && (!getNonLockingActionTakenCodes()
181                 .contains(event.getActionTaken().getActionTaken().getCode()))) {
182             KRADServiceLocatorWeb.getPessimisticLockService().establishWorkflowPessimisticLocking(this);
183         }
184     }
185 
186     /**
187      * @see org.kuali.rice.krad.document.Document#afterActionTaken(org.kuali.rice.kew.api.action.ActionType, org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent)
188      */
189     @Override
190     public void afterActionTaken(ActionType performed, ActionTakenEvent event) {
191         // do nothing
192     }
193 
194     /**
195      * Return the list of actions a user could take on a document which should not result
196      * in the recalculation of the {@link org.kuali.rice.krad.document.authorization.PessimisticLock}s.
197      *
198      * @see #doActionTaken(org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent)
199      */
200     protected List<String> getNonLockingActionTakenCodes() {
201         List<String> actionTakenStatusCodes = new ArrayList<String>();
202         actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_SAVED_CD);
203         actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD);
204         actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_FYI_CD);
205         actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_DENIED_CD);
206         actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_CANCELED_CD);
207         actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_LOG_DOCUMENT_ACTION_CD);
208         return actionTakenStatusCodes;
209     }
210 
211     /**
212      * The the default implementation for afterWorkflowEngineProcess does nothing, but is meant to provide a hook for
213      * documents to implement for other needs.
214      *
215      * @see org.kuali.rice.krad.document.Document#afterWorkflowEngineProcess(boolean)
216      */
217     @Override
218     public void afterWorkflowEngineProcess(boolean successfullyProcessed) {
219         if (KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(
220                 this.getClass().getName()).getUseWorkflowPessimisticLocking()) {
221             if (successfullyProcessed) {
222                 KRADServiceLocatorWeb.getPessimisticLockService().releaseWorkflowPessimisticLocking(this);
223             }
224         }
225     }
226 
227     /**
228      * The the default implementation for beforeWorkflowEngineProcess does nothing, but is meant to provide a hook for
229      * documents to implement for other needs.
230      *
231      * @see org.kuali.rice.krad.document.Document#beforeWorkflowEngineProcess()
232      */
233     @Override
234     public void beforeWorkflowEngineProcess() {
235         // do nothing
236     }
237 
238     /**
239      * The default implementation returns no additional ids for the workflow engine to lock prior to processing.
240      *
241      * @see org.kuali.rice.krad.document.Document#getWorkflowEngineDocumentIdsToLock()
242      */
243     @Override
244     public List<String> getWorkflowEngineDocumentIdsToLock() {
245         return null;
246     }
247 
248     /**
249      * @see org.kuali.rice.krad.document.Copyable#toCopy()
250      */
251     public void toCopy() throws WorkflowException, IllegalStateException {
252         if (!this.getAllowsCopy()) {
253             throw new IllegalStateException(this.getClass().getName() + " does not support document-level copying");
254         }
255         String sourceDocumentHeaderId = getDocumentNumber();
256         setNewDocumentHeader();
257 
258         //clear out notes from previous bo
259         this.notes.clear();
260         addCopyErrorDocumentNote("copied from document " + sourceDocumentHeaderId);
261     }
262 
263     /**
264      * Gets a new document header for this documents type and sets in the document instance.
265      *
266      * @throws org.kuali.rice.kew.api.exception.WorkflowException
267      */
268     protected void setNewDocumentHeader() throws WorkflowException {
269         // collect the header information from the old document
270         Person user = GlobalVariables.getUserSession().getPerson();
271         WorkflowDocument sourceWorkflowDocument
272                 = KRADServiceLocatorWeb.getWorkflowDocumentService().loadWorkflowDocument(getDocumentNumber(), user);
273         String sourceDocumentTypeName = sourceWorkflowDocument.getDocumentTypeName();
274 
275         // initiate the new workflow entry, get the workflow doc
276         WorkflowDocument workflowDocument
277                 = KRADServiceLocatorWeb.getWorkflowDocumentService().createWorkflowDocument(sourceDocumentTypeName, user);
278         UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), workflowDocument);
279 
280         // set new values on the document header, including the document number from which it was copied
281         Document newDocument = KRADServiceLocatorWeb.getDocumentService().getNewDocument(sourceDocumentTypeName);
282         DocumentHeader newDocumentHeader = newDocument.getDocumentHeader();
283         newDocumentHeader.setDocumentTemplateNumber(getDocumentNumber());
284         newDocumentHeader.setDocumentDescription(getDocumentHeader().getDocumentDescription());
285         newDocumentHeader.setOrganizationDocumentNumber(getDocumentHeader().getOrganizationDocumentNumber());
286 
287         // set the new document number on this document
288         try {
289             KRADServiceLocatorWeb.getLegacyDataAdapter().setObjectPropertyDeep(this,
290                     KRADPropertyConstants.DOCUMENT_NUMBER, documentNumber.getClass(), newDocument.getDocumentNumber());
291         } catch (Exception e) {
292             LOG.error("Unable to set document number property in copied document " + this, e);
293             throw new RuntimeException("Unable to set document number property in copied document " + this, e);
294         }
295 
296         // replace the current document header with the new document header
297         setDocumentHeader(newDocument.getDocumentHeader());
298     }
299 
300     /**
301      * Adds a note to the document indicating it was created by a copy or error correction.
302      *
303      * @param noteText - text for note
304      */
305     protected void addCopyErrorDocumentNote(String noteText) {
306         Note note = null;
307         try {
308             note = KRADServiceLocatorWeb.getDocumentService().createNoteFromDocument(this, noteText);
309         } catch (Exception e) {
310             logErrors();
311             throw new RuntimeException("Couldn't create note on copy or error", e);
312         }
313         addNote(note);
314     }
315 
316     /**
317      * @see org.kuali.rice.krad.document.Document#getXmlForRouteReport()
318      */
319     @Override
320     public String getXmlForRouteReport() {
321         prepareForSave();
322         populateDocumentForRouting();
323         return getDocumentHeader().getWorkflowDocument().getApplicationContent();
324     }
325 
326     /**
327      * @see org.kuali.rice.krad.document.Document#populateDocumentForRouting()
328      */
329     @Override
330     public void populateDocumentForRouting() {
331         getDocumentHeader().getWorkflowDocument().setApplicationContent(serializeDocumentToXml());
332     }
333 
334     /**
335      * @see org.kuali.rice.krad.document.Document#serializeDocumentToXml()
336      */
337     @Override
338     public String serializeDocumentToXml() {
339         DocumentSerializerService documentSerializerService = KRADServiceLocatorWeb.getDocumentSerializerService();
340         String xml = documentSerializerService.serializeDocumentToXmlForRouting(this);
341         return xml;
342     }
343 
344     /**
345      * Wraps a document in an instance of KualiDocumentXmlMaterializer, that provides additional metadata for
346      * serialization
347      *
348      * @see org.kuali.rice.krad.document.Document#wrapDocumentWithMetadataForXmlSerialization()
349      */
350     @Override
351     public KualiDocumentXmlMaterializer wrapDocumentWithMetadataForXmlSerialization() {
352         KualiTransactionalDocumentInformation transInfo = new KualiTransactionalDocumentInformation();
353         DocumentInitiator initiator = new DocumentInitiator();
354         String initiatorPrincipalId = getDocumentHeader().getWorkflowDocument().getDocument().getInitiatorPrincipalId();
355         Person initiatorUser = KimApiServiceLocator.getPersonService().getPerson(initiatorPrincipalId);
356         initiator.setPerson(initiatorUser);
357         transInfo.setDocumentInitiator(initiator);
358         KualiDocumentXmlMaterializer xmlWrapper = new KualiDocumentXmlMaterializer();
359         xmlWrapper.setDocument(this);
360         xmlWrapper.setKualiTransactionalDocumentInformation(transInfo);
361         return xmlWrapper;
362     }
363 
364     /**
365      * If workflowProperties have been defined within the data dictionary for this document, then it returns an instance
366      * of
367      * {@link org.kuali.rice.krad.util.documentserializer.BusinessObjectPropertySerializibilityEvaluator} initialized with the properties.  If none have been
368      * defined, then returns
369      * {@link org.kuali.rice.krad.util.documentserializer.AlwaysTruePropertySerializibilityEvaluator}.
370      *
371      * @see org.kuali.rice.krad.document.Document#getDocumentPropertySerizabilityEvaluator()
372      */
373     @Override
374     public PropertySerializabilityEvaluator getDocumentPropertySerizabilityEvaluator() {
375         String docTypeName = getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
376         DocumentEntry documentEntry =
377                 KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(docTypeName);
378         WorkflowProperties workflowProperties = documentEntry.getWorkflowProperties();
379         WorkflowAttributes workflowAttributes = documentEntry.getWorkflowAttributes();
380         return createPropertySerializabilityEvaluator(workflowProperties, workflowAttributes);
381     }
382 
383     protected PropertySerializabilityEvaluator createPropertySerializabilityEvaluator(
384             WorkflowProperties workflowProperties, WorkflowAttributes workflowAttributes) {
385         if (workflowAttributes != null) {
386             return new AlwaysFalsePropertySerializabilityEvaluator();
387         }
388         if (workflowProperties == null) {
389             return new AlwaysTruePropertySerializibilityEvaluator();
390         }
391         PropertySerializabilityEvaluator evaluator = new BusinessObjectPropertySerializibilityEvaluator();
392         evaluator.initializeEvaluatorForDocument(this);
393         return evaluator;
394     }
395 
396     /**
397      * Returns the POJO property name of "this" document in the object returned by {@link
398      * #wrapDocumentWithMetadataForXmlSerialization()}
399      *
400      * @see org.kuali.rice.krad.document.Document#getBasePathToDocumentDuringSerialization()
401      */
402     @Override
403     public String getBasePathToDocumentDuringSerialization() {
404         return "document";
405     }
406 
407     /**
408      * {@inheritDoc}
409      */
410     @Override
411     public DocumentHeader getDocumentHeader() {
412         // during the transition time between OJB and JPA - the OJB hooks are not firing
413         // so, we lazy load the document header.
414         if ((documentHeader == null || documentHeader.getDocumentNumber() == null) && StringUtils.isNotBlank(documentNumber)) {
415             documentHeader = KRADServiceLocatorWeb.getDocumentHeaderService().getDocumentHeaderById(documentNumber);
416         }
417 
418         return this.documentHeader;
419     }
420 
421     /**
422      * {@inheritDoc}
423      */
424     @Override
425     public void setDocumentHeader(DocumentHeader documentHeader) {
426         this.documentHeader = documentHeader;
427     }
428 
429     /**
430      * {@inheritDoc}
431      */
432     @Override
433     public String getDocumentNumber() {
434         return documentNumber;
435     }
436 
437     /**
438      * {@inheritDoc}
439      */
440     @Override
441     public void setDocumentNumber(String documentNumber) {
442         this.documentNumber = documentNumber;
443     }
444 
445     /**
446      * {@inheritDoc}
447      */
448     @Override
449     public List<AdHocRoutePerson> getAdHocRoutePersons() {
450         return adHocRoutePersons;
451     }
452 
453     /**
454      * {@inheritDoc}
455      */
456     @Override
457     public void setAdHocRoutePersons(List<AdHocRoutePerson> adHocRoutePersons) {
458         this.adHocRoutePersons = adHocRoutePersons;
459     }
460 
461     /**
462      * {@inheritDoc}
463      */
464     @Override
465     public List<AdHocRouteWorkgroup> getAdHocRouteWorkgroups() {
466         return adHocRouteWorkgroups;
467     }
468 
469     /**
470      * {@inheritDoc}
471      */
472     @Override
473     public void setAdHocRouteWorkgroups(List<AdHocRouteWorkgroup> adHocRouteWorkgroups) {
474         this.adHocRouteWorkgroups = adHocRouteWorkgroups;
475     }
476 
477     @Override
478     public void postProcessSave(DocumentEvent event) {
479         // TODO Auto-generated method stub
480 
481     }
482 
483     /**
484      * Override this method with implementation specific prepareForSave logic
485      *
486      * @see org.kuali.rice.krad.document.Document#prepareForSave(org.kuali.rice.krad.rules.rule.event.DocumentEvent)
487      */
488     @Override
489     public void prepareForSave(DocumentEvent event) {
490         // do nothing by default
491     }
492 
493     @Override
494     public void validateBusinessRules(DocumentEvent event) {
495         if (GlobalVariables.getMessageMap().hasErrors()) {
496             logErrors();
497             throw new ValidationException("errors occured before business rule");
498         }
499 
500         // perform validation against rules engine
501         LOG.info("invoking rules engine on document " + getDocumentNumber());
502         boolean isValid = true;
503         isValid = KRADServiceLocatorWeb.getKualiRuleService().applyRules(event);
504 
505         // check to see if the br eval passed or failed
506         if (!isValid) {
507             logErrors();
508             // TODO: better error handling at the lower level and a better error message are
509             // needed here
510             throw new ValidationException("business rule evaluation failed");
511         } else if (GlobalVariables.getMessageMap().hasErrors()) {
512             logErrors();
513             throw new ValidationException(
514                     "Unreported errors occured during business rule evaluation (rule developer needs to put meaningful error messages into global ErrorMap)");
515         }
516         LOG.debug("validation completed");
517 
518     }
519 
520     /**
521      * This method logs errors.
522      */
523     protected void logErrors() {
524         if (LOG.isInfoEnabled()) {
525             if (GlobalVariables.getMessageMap().hasErrors()) {
526 
527                 for (Iterator<Map.Entry<String, List<ErrorMessage>>> i =
528                              GlobalVariables.getMessageMap().getAllPropertiesAndErrors().iterator(); i.hasNext(); ) {
529                     Map.Entry<String, List<ErrorMessage>> e = i.next();
530 
531                     StringBuffer logMessage = new StringBuffer();
532                     logMessage.append("[" + e.getKey() + "] ");
533                     boolean first = true;
534 
535                     List<ErrorMessage> errorList = e.getValue();
536                     for (Iterator<ErrorMessage> j = errorList.iterator(); j.hasNext(); ) {
537                         ErrorMessage em = j.next();
538 
539                         if (first) {
540                             first = false;
541                         } else {
542                             logMessage.append(";");
543                         }
544                         logMessage.append(em);
545                     }
546 
547                     LOG.info(logMessage);
548                 }
549             }
550         }
551     }
552 
553     /**
554      * Hook for override
555      *
556      * @see org.kuali.rice.krad.document.Document#generateSaveEvents()
557      */
558     @Override
559     public List<DocumentEvent> generateSaveEvents() {
560         return new ArrayList<DocumentEvent>();
561     }
562 
563     /**
564      * @see org.kuali.rice.krad.document.Document#doRouteStatusChange(org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange)
565      */
566     @Override
567     public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
568         // do nothing
569     }
570 
571     /**
572      * Returns the business object with which notes related to this document should be associated.
573      * By default, the {@link org.kuali.rice.krad.bo.DocumentHeader} of this document will be returned as the note target.
574      *
575      * <p>Sub classes can override this method if they want notes to be associated with something
576      * other than the document header.  If this method is overridden, the {@link #getNoteType()}
577      * method should be overridden to return {@link org.kuali.rice.krad.util.NoteType#BUSINESS_OBJECT}
578      *
579      * @return Returns the documentBusinessObject.
580      */
581     @Override
582     public GloballyUnique getNoteTarget() {
583         return getDocumentHeader();
584     }
585 
586     /**
587      * Returns the {@link org.kuali.rice.krad.util.NoteType} to use for notes associated with this document.
588      * By default this returns {@link org.kuali.rice.krad.util.NoteType#DOCUMENT_HEADER} since notes are
589      * associated with the {@link org.kuali.rice.krad.bo.DocumentHeader} record by default.
590      *
591      * <p>The case in which this should be overridden is if {@link #getNoteTarget()} is
592      * overridden to return an object other than the DocumentHeader.
593      *
594      * @return the note type to use for notes associated with this document
595      * @see org.kuali.rice.krad.document.Document#getNoteType()
596      */
597     @Override
598     public NoteType getNoteType() {
599         return NoteType.DOCUMENT_HEADER;
600     }
601 
602     /**
603      * @see org.kuali.rice.krad.document.Document#addNote(org.kuali.rice.krad.bo.Note)
604      */
605     @Override
606     public void addNote(Note note) {
607         if (note == null) {
608             throw new IllegalArgumentException("Note cannot be null.");
609         }
610         notes.add(note);
611     }
612 
613     /**
614      * @see org.kuali.rice.krad.document.Document#removeNote(org.kuali.rice.krad.bo.Note)
615      */
616     @Override
617     public boolean removeNote(Note note) {
618         if (note == null) {
619             throw new IllegalArgumentException("Note cannot be null.");
620         }
621         return notes.remove(note);
622     }
623 
624     /**
625      * @see org.kuali.rice.krad.document.Document#getNote(int)
626      */
627     @Override
628     public Note getNote(int index) {
629         return notes.get(index);
630     }
631 
632     /**
633      * @see org.kuali.rice.krad.document.Document#getNotes()
634      */
635     @Override
636     public List<Note> getNotes() {
637         if (CollectionUtils.isEmpty(notes) && getNoteType().equals(NoteType.BUSINESS_OBJECT) && StringUtils.isNotBlank(
638                 getNoteTarget().getObjectId())) {
639             notes = getNoteService().getByRemoteObjectId(getNoteTarget().getObjectId());
640         }
641 
642         return notes;
643     }
644 
645     /**
646      * @see org.kuali.rice.krad.document.Document#setNotes(java.util.List)
647      */
648     @Override
649     public void setNotes(List<Note> notes) {
650         if (notes == null) {
651             throw new IllegalArgumentException("List of notes must be non-null.");
652         }
653         this.notes = notes;
654     }
655 
656     /**
657      * {@inheritDoc}
658      */
659     @Override
660     public List<ActionRequest> getActionRequests() {
661         return KewApiServiceLocator.getWorkflowDocumentService().getPendingActionRequests(getDocumentNumber());
662     }
663 
664     /**
665      * {@inheritDoc}
666      */
667     @Override
668     public String getSuperUserAnnotation() {
669         return superUserAnnotation;
670     }
671 
672     /**
673      * {@inheritDoc}
674      */
675     @Override
676     public void setSuperUserAnnotation(String superUserAnnotation) {
677         this.superUserAnnotation = superUserAnnotation;
678     }
679 
680     /**
681      * Loads the KRAD document header via the document header service.
682      *
683      * @see org.kuali.rice.krad.bo.PersistableBusinessObjectBase#postLoad()
684      */
685     @PostLoad
686     protected void postLoad() {
687         documentHeader = KRADServiceLocatorWeb.getDocumentHeaderService().getDocumentHeaderById(documentNumber);
688         refreshPessimisticLocks();
689     }
690 
691     /**
692      * Save the KRAD document header via the document header service.
693      */
694     @PrePersist
695     protected void prePersist() {
696         super.prePersist();
697         // KRAD/JPA - have to change the handle to object to that just saved
698 
699         /* Modified by 'Sheik Salahudeen' for fixing the 'workflow document is null' issue in KRAD Transaction document with JPA.
700         * Removed the code for saving document header. Because document header will be stored by document service.*/
701 
702         // documentHeader = KRADServiceLocatorWeb.getDocumentHeaderService().saveDocumentHeader(documentHeader);
703     }
704 
705     /**
706      * This overridden method is used to delete the {@link org.kuali.rice.krad.bo.DocumentHeader} ob    ject due to the system not being able to
707      * manage the {@link org.kuali.rice.krad.bo.DocumentHeader} object via mapping files
708      *
709      * @see org.kuali.rice.krad.bo.PersistableBusinessObjectBase#postRemove()
710      */
711     @PostRemove
712     protected void postRemove() {
713         KRADServiceLocatorWeb.getDocumentHeaderService().deleteDocumentHeader(getDocumentHeader());
714     }
715 
716     /**
717      * @see org.kuali.rice.krad.document.Document#getPessimisticLocks()
718      */
719     @Override
720     public List<PessimisticLock> getPessimisticLocks() {
721         return pessimisticLocks;
722     }
723 
724     /**
725      * @see org.kuali.rice.krad.document.Document#refreshPessimisticLocks()
726      */
727     @Override
728     public void refreshPessimisticLocks() {
729         pessimisticLocks = KRADServiceLocatorWeb.getPessimisticLockService().getPessimisticLocksForDocument(documentNumber);
730     }
731 
732     /**
733      * @param pessimisticLocks the PessimisticLock objects to set
734      */
735     public void setPessimisticLocks(List<PessimisticLock> pessimisticLocks) {
736         this.pessimisticLocks = pessimisticLocks;
737     }
738 
739     /**
740      * @see org.kuali.rice.krad.document.Document#addPessimisticLock(org.kuali.rice.krad.document.authorization.PessimisticLock)
741      */
742     @Override
743     public void addPessimisticLock(PessimisticLock lock) {
744         pessimisticLocks.add(lock);
745     }
746 
747     /**
748      * @see org.kuali.rice.krad.document.Document#getLockClearingMethodNames()
749      */
750     @Override
751     @Deprecated
752     public List<String> getLockClearingMethodNames() {
753         return getLockClearningMethodNames();
754     }
755 
756     /**
757      * @see org.kuali.rice.krad.document.Document#getLockClearingMethodNames()
758      */
759     @Override
760     @Deprecated
761     public List<String> getLockClearningMethodNames() {
762         List<String> methodToCalls = new ArrayList<String>();
763         methodToCalls.add(KRADConstants.CLOSE_METHOD);
764         methodToCalls.add(KRADConstants.CANCEL_METHOD);
765         //        methodToCalls.add(RiceConstants.BLANKET_APPROVE_METHOD);
766         methodToCalls.add(KRADConstants.ROUTE_METHOD);
767         methodToCalls.add(KRADConstants.APPROVE_METHOD);
768         methodToCalls.add(KRADConstants.DISAPPROVE_METHOD);
769         methodToCalls.add(KRADConstants.ACKNOWLEDGE_METHOD);
770         return methodToCalls;
771     }
772 
773     /**
774      * This default implementation simply returns false to indicate that custom lock descriptors are not supported by
775      * DocumentBase. If custom lock
776      * descriptors are needed, the appropriate subclasses should override this method.
777      *
778      * @see org.kuali.rice.krad.document.Document#useCustomLockDescriptors()
779      */
780     @Override
781     public boolean useCustomLockDescriptors() {
782         return false;
783     }
784 
785     /**
786      * This default implementation just throws a PessimisticLockingException. Subclasses of DocumentBase that need
787      * support for custom lock descriptors
788      * should override this method.
789      *
790      * @see org.kuali.rice.krad.document.Document#getCustomLockDescriptor(org.kuali.rice.kim.api.identity.Person)
791      */
792     @Override
793     public String getCustomLockDescriptor(Person user) {
794         throw new PessimisticLockingException("Document " + getDocumentNumber() +
795                 " is using pessimistic locking with custom lock descriptors, but the document class has not overriden the getCustomLockDescriptor method");
796     }
797 
798     protected AttachmentService getAttachmentService() {
799         if (attachmentService == null) {
800             attachmentService = KRADServiceLocator.getAttachmentService();
801         }
802         return attachmentService;
803     }
804 
805     protected NoteService getNoteService() {
806         if (noteService == null) {
807             noteService = KRADServiceLocator.getNoteService();
808         }
809         return noteService;
810     }
811 
812     /**
813      * Overrides this OJB method to accept the no-longer-bound documentHeader reference
814      * and perform the refresh via services instead of via OJB.
815      *
816      * For any other property, it works as before.
817      *
818      * @deprecated This is a KNS/OJB-related method.  It should not be used on KRAD/JPA-based documents.
819      * @see org.kuali.rice.krad.bo.PersistableBusinessObjectBase#refreshReferenceObject(java.lang.String)
820      */
821     @Deprecated
822     public void refreshReferenceObject(String referenceObjectName) {
823         if ( StringUtils.equals( referenceObjectName, "documentHeader" ) ) {
824             documentHeader = KRADServiceLocatorWeb.getDocumentHeaderService().getDocumentHeaderById(documentNumber);
825         } else {
826             super.refreshReferenceObject(referenceObjectName);
827         }
828     }
829 }