View Javadoc

1   /*
2    * Copyright 2005-2007 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.document;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.log4j.Logger;
20  import org.kuali.rice.kew.api.KewApiServiceLocator;
21  import org.kuali.rice.kew.dto.ActionTakenEventDTO;
22  import org.kuali.rice.kew.dto.DocumentRouteLevelChangeDTO;
23  import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO;
24  import org.kuali.rice.kew.exception.WorkflowException;
25  import org.kuali.rice.kew.util.KEWConstants;
26  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
27  import org.kuali.rice.kim.bo.Person;
28  import org.kuali.rice.krad.bo.AdHocRoutePerson;
29  import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
30  import org.kuali.rice.krad.bo.DocumentHeader;
31  import org.kuali.rice.krad.bo.Note;
32  import org.kuali.rice.krad.bo.PersistableBusinessObject;
33  import org.kuali.rice.krad.bo.PersistableBusinessObjectBase;
34  import org.kuali.rice.krad.datadictionary.DocumentEntry;
35  import org.kuali.rice.krad.datadictionary.WorkflowAttributes;
36  import org.kuali.rice.krad.datadictionary.WorkflowProperties;
37  import org.kuali.rice.krad.document.authorization.PessimisticLock;
38  import org.kuali.rice.krad.exception.PessimisticLockingException;
39  import org.kuali.rice.krad.exception.ValidationException;
40  import org.kuali.rice.krad.rule.event.KualiDocumentEvent;
41  import org.kuali.rice.krad.service.AttachmentService;
42  import org.kuali.rice.krad.service.DocumentSerializerService;
43  import org.kuali.rice.krad.service.KRADServiceLocator;
44  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
45  import org.kuali.rice.krad.service.NoteService;
46  import org.kuali.rice.krad.util.ErrorMessage;
47  import org.kuali.rice.krad.util.GlobalVariables;
48  import org.kuali.rice.krad.util.KRADConstants;
49  import org.kuali.rice.krad.util.KRADPropertyConstants;
50  import org.kuali.rice.krad.util.NoteType;
51  import org.kuali.rice.krad.util.ObjectUtils;
52  import org.kuali.rice.krad.util.documentserializer.AlwaysFalsePropertySerializabilityEvaluator;
53  import org.kuali.rice.krad.util.documentserializer.AlwaysTruePropertySerializibilityEvaluator;
54  import org.kuali.rice.krad.util.documentserializer.BusinessObjectPropertySerializibilityEvaluator;
55  import org.kuali.rice.krad.util.documentserializer.PropertySerializabilityEvaluator;
56  import org.kuali.rice.krad.workflow.DocumentInitiator;
57  import org.kuali.rice.krad.workflow.KualiDocumentXmlMaterializer;
58  import org.kuali.rice.krad.workflow.KualiTransactionalDocumentInformation;
59  import org.springframework.util.AutoPopulatingList;
60  
61  import javax.persistence.CascadeType;
62  import javax.persistence.Column;
63  import javax.persistence.FetchType;
64  import javax.persistence.Id;
65  import javax.persistence.JoinColumn;
66  import javax.persistence.MappedSuperclass;
67  import javax.persistence.OneToMany;
68  import javax.persistence.OneToOne;
69  import javax.persistence.Transient;
70  import java.util.ArrayList;
71  import java.util.Iterator;
72  import java.util.List;
73  import java.util.Map;
74  
75  
76  /**
77   * @see Document
78   */
79  @MappedSuperclass
80  public abstract class DocumentBase extends PersistableBusinessObjectBase implements Document {
81      private static final Logger LOG = Logger.getLogger(DocumentBase.class);
82      
83      @Id
84      @Column(name="DOC_HDR_ID")
85      protected String documentNumber;
86      @OneToOne(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.MERGE})
87  	@JoinColumn(name="DOC_HDR_ID", insertable=false, updatable=false)
88      protected DocumentHeader documentHeader;    
89  
90      @OneToMany(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.MERGE})
91  	@JoinColumn(name="DOC_HDR_ID", insertable=false, updatable=false)
92      private List<PessimisticLock> pessimisticLocks;
93  
94      @Transient
95      private List<AdHocRoutePerson> adHocRoutePersons;
96      @Transient
97      private List<AdHocRouteWorkgroup> adHocRouteWorkgroups;
98      @Transient
99      private List<Note> notes;
100     
101     private transient NoteService noteService;
102     private transient AttachmentService attachmentService;
103 
104     /**
105      * Constructs a DocumentBase.java.
106      */
107     public DocumentBase() {
108         try {
109             // create a new document header object
110             Class<? extends DocumentHeader> documentHeaderClass = KRADServiceLocatorWeb.getDocumentHeaderService().getDocumentHeaderBaseClass();
111             setDocumentHeader(documentHeaderClass.newInstance());
112             pessimisticLocks = new ArrayList<PessimisticLock>();
113             adHocRoutePersons = new ArrayList<AdHocRoutePerson>();
114             adHocRouteWorkgroups = new ArrayList<AdHocRouteWorkgroup>();
115             notes = new ArrayList<Note>();
116         }
117         catch (IllegalAccessException e) {
118             throw new RuntimeException("Error instantiating DocumentHeader", e);
119         }
120         catch (InstantiationException e) {
121             throw new RuntimeException("Error instantiating DocumentHeader", e);
122         }
123     }
124 
125     /**
126      * @see org.kuali.rice.krad.document.Document#getAllowsCopy()
127      */
128     public boolean getAllowsCopy() {
129         // TODO Auto-generated method stub
130         return false;
131     }
132 
133     /**
134      * This is the default document title implementation. It concatenates the document's data dictionary file label attribute and
135      * the document's document header description together. This title is used to populate workflow and will show up in document
136      * search results and user action lists.
137      *
138      * @see org.kuali.rice.krad.document.Document#getDocumentTitle()
139      */
140     public String getDocumentTitle() {
141         String documentTypeLabel = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(this.getDocumentHeader().getWorkflowDocument().getDocumentTypeName()).getLabel();
142         if (null == documentTypeLabel) {
143             documentTypeLabel = "";
144         }
145     
146         String description = this.getDocumentHeader().getDocumentDescription();
147         if (null == description) {
148             description = "";
149         }
150     
151         return documentTypeLabel + " - " + description;
152     }
153 
154     /**
155      * Uses the persistence service's implementation of OJB's retrieveNonKey() fields method.
156      *
157      * @see org.kuali.rice.krad.bo.BusinessObject#refresh()
158      */
159     @Override
160     public void refresh() {
161         KRADServiceLocator.getPersistenceService().retrieveNonKeyFields(this);
162     }
163 
164     /**
165      * Checks to see if the objectId value is empty. If so, it will try to refresh the object from the DB.
166      *
167      * @see org.kuali.rice.krad.document.Document#refreshIfEmpty()
168      */
169     public void refreshIfEmpty() {
170         if (null == this.getDocumentHeader()) {
171             this.refresh();
172         }
173         else if (StringUtils.isEmpty(this.getDocumentHeader().getObjectId())) {
174             this.refresh();
175         }
176     }
177 
178     /**
179      * Uses the persistence service to retrieve a reference object of a parent.
180      *
181      * @see org.kuali.rice.krad.document.Document#refreshReferenceObject(java.lang.String)
182      */
183     @Override
184     public void refreshReferenceObject(String referenceObjectName) {
185         KRADServiceLocator.getPersistenceService().retrieveReferenceObject(this, referenceObjectName);
186     }
187 
188     /**
189      * @see org.kuali.rice.krad.document.Document#prepareForSave()
190      */
191     public void prepareForSave() {
192         // do nothing
193     }
194 
195     /**
196      * @see org.kuali.rice.krad.document.Document#processAfterRetrieve()
197      */
198     public void processAfterRetrieve() {
199     	// do nothing
200     }
201 
202     /**
203      * The the default implementation for RouteLevelChange does nothing, but is meant to provide a hook for documents to implement
204      * for other needs.
205      *
206      * @see org.kuali.rice.krad.document.Document#doRouteLevelChange(org.kuali.rice.kew.dto.DocumentRouteLevelChangeDTO)
207      */
208     public void doRouteLevelChange(DocumentRouteLevelChangeDTO levelChangeEvent) {
209         // do nothing
210     }
211     
212     /**
213      * @see org.kuali.rice.krad.document.Document#doActionTaken(org.kuali.rice.kew.dto.ActionTakenEventDTO)
214      */
215     public void doActionTaken(ActionTakenEventDTO event) {
216         if ( (KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(this.getClass().getName()).getUseWorkflowPessimisticLocking()) && (!getNonLockingActionTakenCodes().contains(event.getActionTaken().getActionTaken())) ) {
217             //DocumentAuthorizer documentAuthorizer = KRADServiceLocatorInternal.getDocumentAuthorizationService().getDocumentAuthorizer(this);
218             //documentAuthorizer.establishWorkflowPessimisticLocking(this);
219         	KRADServiceLocatorWeb.getPessimisticLockService().establishWorkflowPessimisticLocking(this);
220         }
221     }
222     
223     protected List<String> getNonLockingActionTakenCodes() {
224         List<String> actionTakenStatusCodes = new ArrayList<String>();
225         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_SAVED_CD);
226         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_ACKNOWLEDGED_CD);
227         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_FYI_CD);
228         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_DENIED_CD);
229         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_CANCELED_CD);
230         actionTakenStatusCodes.add(KEWConstants.ACTION_TAKEN_LOG_DOCUMENT_ACTION_CD);
231         return actionTakenStatusCodes;
232     }
233 
234     /**
235      * The the default implementation for afterWorkflowEngineProcess does nothing, but is meant to provide a hook for
236      * documents to implement for other needs.
237      * 
238      * @see org.kuali.rice.krad.document.Document#afterWorkflowEngineProcess(boolean)
239      */
240     public void afterWorkflowEngineProcess(boolean successfullyProcessed) {
241         if (KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(this.getClass().getName()).getUseWorkflowPessimisticLocking()) {
242             if (successfullyProcessed) {
243                 //DocumentAuthorizer documentAuthorizer = KRADServiceLocatorInternal.getDocumentAuthorizationService().getDocumentAuthorizer(this);
244                 //documentAuthorizer.releaseWorkflowPessimisticLocking(this);
245             	KRADServiceLocatorWeb.getPessimisticLockService().releaseWorkflowPessimisticLocking(this);
246             }
247         }
248     }
249 
250     /**
251      * The the default implementation for beforeWorkflowEngineProcess does nothing, but is meant to provide a hook for
252      * documents to implement for other needs.
253      * 
254      * @see org.kuali.rice.krad.document.Document#beforeWorkflowEngineProcess()
255      */
256     public void beforeWorkflowEngineProcess() {
257     // do nothing
258     }
259     
260     
261 
262     /**
263      * The default implementation returns no additional ids for the workflow engine to lock prior to processing.
264      * 
265      * @see org.kuali.rice.krad.document.Document#getWorkflowEngineDocumentIdsToLock()
266      */
267     public List<Long> getWorkflowEngineDocumentIdsToLock() {
268 		return null;
269 	}
270 
271 	/**
272      * @see org.kuali.rice.krad.document.Copyable#toCopy()
273      */
274     public void toCopy() throws WorkflowException, IllegalStateException {
275         if (!this.getAllowsCopy()) {
276             throw new IllegalStateException(this.getClass().getName() + " does not support document-level copying");
277         }
278         String sourceDocumentHeaderId = getDocumentNumber();
279         setNewDocumentHeader();
280                 
281         getDocumentHeader().setDocumentTemplateNumber(sourceDocumentHeaderId);
282 
283         addCopyErrorDocumentNote("copied from document " + sourceDocumentHeaderId);
284     }
285 
286     /**
287      * Gets a new document header for this documents type and sets in the document instance.
288      * 
289      * @throws WorkflowException
290      */
291     protected void setNewDocumentHeader() throws WorkflowException {
292         TransactionalDocument newDoc = (TransactionalDocument) KRADServiceLocatorWeb.getDocumentService().getNewDocument(getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
293         newDoc.getDocumentHeader().setDocumentDescription(getDocumentHeader().getDocumentDescription());
294         newDoc.getDocumentHeader().setOrganizationDocumentNumber(getDocumentHeader().getOrganizationDocumentNumber());
295 
296         try {
297             ObjectUtils.setObjectPropertyDeep(this, KRADPropertyConstants.DOCUMENT_NUMBER, documentNumber.getClass(), newDoc.getDocumentNumber());
298         }
299         catch (Exception e) {
300             LOG.error("Unable to set document number property in copied document " + e.getMessage(),e);
301             throw new RuntimeException("Unable to set document number property in copied document " + e.getMessage(),e);
302         }
303 
304         // replace current documentHeader with new documentHeader
305         setDocumentHeader(newDoc.getDocumentHeader());
306     }
307 
308     /**
309      * Adds a note to the document indicating it was created by a copy or error correction.
310      * 
311      * @param noteText - text for note
312      */
313     protected void addCopyErrorDocumentNote(String noteText) {
314         Note note = null;
315         try {
316             note = KRADServiceLocatorWeb.getDocumentService().createNoteFromDocument(this,noteText);
317         }
318         catch (Exception e) {
319          logErrors();
320          throw new RuntimeException("Couldn't create note on copy or error",e);
321         }
322         addNote(note);
323     }
324 
325     /**
326      * @see org.kuali.rice.krad.document.Document#getXmlForRouteReport()
327      */
328     public String getXmlForRouteReport() {
329 	prepareForSave();
330 	populateDocumentForRouting();
331 	return getDocumentHeader().getWorkflowDocument().getApplicationContent();
332     }
333 
334     /**
335      * @see org.kuali.rice.krad.document.Document#populateDocumentForRouting()
336      */
337     public void populateDocumentForRouting() {
338         getDocumentHeader().getWorkflowDocument().setApplicationContent(serializeDocumentToXml());
339     }
340     
341     /**
342      * @see org.kuali.rice.krad.document.Document#serializeDocumentToXml()
343      */
344     public String serializeDocumentToXml() {
345         DocumentSerializerService documentSerializerService = KRADServiceLocatorWeb.getDocumentSerializerService();
346         String xml = documentSerializerService.serializeDocumentToXmlForRouting(this);
347         return xml;
348     }
349 
350     /**
351      * Wraps a document in an instance of KualiDocumentXmlMaterializer, that provides additional metadata for serialization
352      * 
353      * @see org.kuali.rice.krad.document.Document#wrapDocumentWithMetadataForXmlSerialization()
354      */
355     public KualiDocumentXmlMaterializer wrapDocumentWithMetadataForXmlSerialization() {
356         KualiTransactionalDocumentInformation transInfo = new KualiTransactionalDocumentInformation();
357         DocumentInitiator initiator = new DocumentInitiator();
358         String initiatorPrincipalId = getDocumentHeader().getWorkflowDocument().getDocument().getInitiatorPrincipalId();
359         Person initiatorUser = KimApiServiceLocator.getPersonService().getPerson(initiatorPrincipalId);
360         initiator.setPerson(initiatorUser);
361         transInfo.setDocumentInitiator(initiator);
362         KualiDocumentXmlMaterializer xmlWrapper = new KualiDocumentXmlMaterializer();
363         xmlWrapper.setDocument(this);
364         xmlWrapper.setKualiTransactionalDocumentInformation(transInfo);
365         return xmlWrapper;
366     }
367 
368     /**
369      * If workflowProperties have been defined within the data dictionary for this document, then it returns an instance of 
370      * {@link BusinessObjectPropertySerializibilityEvaluator} initialized with the properties.  If none have been defined, then returns 
371      * {@link AlwaysTruePropertySerializibilityEvaluator}.
372      * 
373      * @see org.kuali.rice.krad.document.Document#getDocumentPropertySerizabilityEvaluator()
374      */
375     public PropertySerializabilityEvaluator getDocumentPropertySerizabilityEvaluator() {
376         String docTypeName = getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
377         DocumentEntry documentEntry = 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(WorkflowProperties workflowProperties, WorkflowAttributes workflowAttributes) {
384     	if (workflowAttributes != null) {
385     		return new AlwaysFalsePropertySerializabilityEvaluator();
386     	}
387     	if (workflowProperties == null) {
388     		return new AlwaysTruePropertySerializibilityEvaluator();
389     	}
390     	PropertySerializabilityEvaluator evaluator = new BusinessObjectPropertySerializibilityEvaluator();
391     	evaluator.initializeEvaluatorForDocument(this);
392     	return evaluator;
393     }
394     
395     /**
396      * Returns the POJO property name of "this" document in the object returned by {@link #wrapDocumentWithMetadataForXmlSerialization()}
397      * 
398      * @see org.kuali.rice.krad.document.Document#getBasePathToDocumentDuringSerialization()
399      */
400     public String getBasePathToDocumentDuringSerialization() {
401         return "document";
402     }
403     
404     
405     /**
406      * @see org.kuali.rice.krad.document.Document#getDocumentHeader()
407      */
408     public DocumentHeader getDocumentHeader() {
409         return this.documentHeader;
410     }
411 
412     /**
413      * @see org.kuali.rice.krad.document.Document#setDocumentHeader(org.kuali.rice.krad.document.DocumentHeader)
414      */
415     public void setDocumentHeader(DocumentHeader documentHeader) {
416         this.documentHeader = documentHeader;
417     }
418 
419     /**
420      * @see org.kuali.rice.krad.document.Document#getDocumentNumber()
421      */
422     public String getDocumentNumber() {
423         return documentNumber;
424     }
425 
426     /**
427      * @see org.kuali.rice.krad.document.Document#setDocumentNumber(java.lang.String)
428      */
429     public void setDocumentNumber(String documentNumber) {
430         this.documentNumber = documentNumber;
431     }
432 
433     /**
434      * @see org.kuali.rice.krad.document.Document#getAdHocRoutePersons()
435      */
436     public List<AdHocRoutePerson> getAdHocRoutePersons() {
437         return adHocRoutePersons;
438     }
439 
440     /**
441      * @see org.kuali.rice.krad.document.Document#setAdHocRoutePersons(java.util.List)
442      */
443     public void setAdHocRoutePersons(List<AdHocRoutePerson> adHocRoutePersons) {
444         this.adHocRoutePersons = adHocRoutePersons;
445 }
446     /**
447      * @see org.kuali.rice.krad.document.Document#getAdHocRouteWorkgroups()
448      */
449     public List<AdHocRouteWorkgroup> getAdHocRouteWorkgroups() {
450         return adHocRouteWorkgroups;
451     }
452 
453     /**
454      * @see org.kuali.rice.krad.document.Document#setAdHocRouteWorkgroups(java.util.List)
455      */
456     public void setAdHocRouteWorkgroups(List<AdHocRouteWorkgroup> adHocRouteWorkgroups) {
457         this.adHocRouteWorkgroups = adHocRouteWorkgroups;
458     }
459 
460     public void postProcessSave(KualiDocumentEvent event) {
461         // TODO Auto-generated method stub
462 
463 	}
464 
465     /**
466      * Override this method with implementation specific prepareForSave logic
467      * 
468      * @see org.kuali.rice.krad.document.Document#prepareForSave(org.kuali.rice.krad.rule.event.KualiDocumentEvent)
469      */
470     public void prepareForSave(KualiDocumentEvent event) {
471     	// do nothing by default
472     }
473 
474     public void validateBusinessRules(KualiDocumentEvent event) {
475         if (GlobalVariables.getMessageMap().hasErrors()) {
476             logErrors();
477             throw new ValidationException("errors occured before business rule");
478         }
479 
480         // perform validation against rules engine
481         LOG.info("invoking rules engine on document " + getDocumentNumber());
482         boolean isValid = true;
483         isValid = KRADServiceLocatorWeb.getKualiRuleService().applyRules(event);
484 
485         // check to see if the br eval passed or failed
486         if (!isValid) {
487             logErrors();
488             // TODO: better error handling at the lower level and a better error message are
489             // needed here
490             throw new ValidationException("business rule evaluation failed");
491         }
492         else if (GlobalVariables.getMessageMap().hasErrors()) {
493             logErrors();
494             throw new ValidationException("Unreported errors occured during business rule evaluation (rule developer needs to put meaningful error messages into global ErrorMap)");
495         }
496         LOG.debug("validation completed");
497 
498     }
499 
500     /**
501      * This method logs errors.
502      */
503     protected void logErrors() {
504     	if ( LOG.isInfoEnabled() ) {
505 	        if (GlobalVariables.getMessageMap().hasErrors()) {
506 	
507 	            for (Iterator<Map.Entry<String, AutoPopulatingList<ErrorMessage>>> i = GlobalVariables.getMessageMap().getAllPropertiesAndErrors().iterator(); i.hasNext();) {
508 	                Map.Entry<String, AutoPopulatingList<ErrorMessage>> e = i.next();
509 	
510 	                StringBuffer logMessage = new StringBuffer();
511 	                logMessage.append("[" + e.getKey() + "] ");
512 	                boolean first = true;
513 	
514 	                AutoPopulatingList<ErrorMessage> errorList = e.getValue();
515 	                for (Iterator<ErrorMessage> j = errorList.iterator(); j.hasNext();) {
516 	                    ErrorMessage em = j.next();
517 	
518 	                    if (first) {
519 	                        first = false;
520 	                    }
521 	                    else {
522 	                        logMessage.append(";");
523 	                    }
524 	                    logMessage.append(em);
525 	                }
526 	
527 	                LOG.info(logMessage);
528 	            }
529 	        }
530     	}
531     }
532 
533     /**
534      * Hook for override
535      * 
536      * @see org.kuali.rice.krad.document.Document#generateSaveEvents()
537      */
538     public List<KualiDocumentEvent> generateSaveEvents() {
539         return new ArrayList<KualiDocumentEvent>();
540     }
541 
542     /**
543      * @see org.kuali.rice.krad.document.Document#doRouteStatusChange(org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO)
544      */
545     public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent) {
546         // do nothing
547     }
548     
549     /**
550      * Returns the business object with which notes related to this document should be associated.
551      * By default, the {@link DocumentHeader} of this document will be returned as the note target.
552      * 
553      * <p>Sub classes can override this method if they want notes to be associated with something
554      * other than the document header.  If this method is overridden, the {@link #getNoteType()}
555      * method should be overridden to return {@link NoteType#BUSINESS_OBJECT}
556      * 
557      * @return Returns the documentBusinessObject.
558      */
559     @Override
560     public PersistableBusinessObject getNoteTarget() {
561         return getDocumentHeader();
562     }
563     
564     /**
565 	 * Returns the {@link NoteType} to use for notes associated with this document.
566 	 * By default this returns {@link NoteType#DOCUMENT_HEADER} since notes are
567 	 * associated with the {@link DocumentHeader} record by default.
568 	 * 
569 	 * <p>The case in which this should be overridden is if {@link #getNoteTarget()} is
570 	 * overridden to return an object other than the DocumentHeader.
571 	 *
572 	 * @return the note type to use for notes associated with this document
573 	 * 
574 	 * @see org.kuali.rice.krad.document.Document#getNoteType()
575 	 */
576 	@Override
577 	public NoteType getNoteType() {
578 		return NoteType.DOCUMENT_HEADER;
579 	}
580 
581 	/**
582 	 * @see org.kuali.rice.krad.document.Document#addNote(org.kuali.rice.krad.bo.Note)
583 	 */
584     @Override
585 	public void addNote(Note note) {
586     	if (note == null) {
587     		throw new IllegalArgumentException("Note cannot be null.");
588     	}
589 		notes.add(note);
590 	}
591 
592     /**
593      * @see org.kuali.rice.krad.document.Document#removeNote(org.kuali.rice.krad.bo.Note)
594      */
595 	@Override
596 	public boolean removeNote(Note note) {
597 		if (note == null) {
598     		throw new IllegalArgumentException("Note cannot be null.");
599     	}
600 		return notes.remove(note);
601 	}
602 
603 	/**
604 	 * @see org.kuali.rice.krad.document.Document#getNote(int)
605 	 */
606 	@Override
607 	public Note getNote(int index) {
608 		return notes.get(index);
609 	}
610 
611 	/**
612 	 * @see org.kuali.rice.krad.document.Document#getNotes()
613 	 */
614 	@Override
615 	public List<Note> getNotes() {
616 		return notes;
617 	}
618 	
619 	/**
620 	 * @see org.kuali.rice.krad.document.Document#setNotes(java.util.List)
621 	 */
622 	@Override
623 	public void setNotes(List<Note> notes) {
624 		if (notes == null) {
625 			throw new IllegalArgumentException("List of notes must be non-null.");
626 		}
627 		this.notes = notes;
628 	}
629 
630     /**
631      * @see org.kuali.rice.krad.document.Document#getPessimisticLocks()
632      */
633     public List<PessimisticLock> getPessimisticLocks() {
634         return this.pessimisticLocks;
635     }
636     
637     /**
638      * @see org.kuali.rice.krad.document.Document#refreshPessimisticLocks()
639      * @deprecated
640      * This is not needed with the relationship set up with JPA annotations
641      */
642     @Deprecated 
643     public void refreshPessimisticLocks() {
644         this.pessimisticLocks.clear();
645         this.pessimisticLocks = KRADServiceLocatorWeb.getPessimisticLockService().getPessimisticLocksForDocument(this.documentNumber);
646     }
647 
648     /**
649      * @param pessimisticLocks the PessimisticLock objects to set
650      */
651     public void setPessimisticLocks(List<PessimisticLock> pessimisticLocks) {
652         this.pessimisticLocks = pessimisticLocks;
653     }
654     
655     /**
656      * @see org.kuali.rice.krad.document.Document#addPessimisticLock(org.kuali.rice.krad.document.authorization.PessimisticLock)
657      */
658     public void addPessimisticLock(PessimisticLock lock) {
659         this.pessimisticLocks.add(lock);
660     }
661     
662     /**
663      * @see org.kuali.rice.krad.document.Document#getLockClearningMethodNames()
664      */
665     public List<String> getLockClearningMethodNames() {
666         List<String> methodToCalls = new ArrayList<String>();
667         methodToCalls.add(KRADConstants.CLOSE_METHOD);
668         methodToCalls.add(KRADConstants.CANCEL_METHOD);
669 //        methodToCalls.add(RiceConstants.BLANKET_APPROVE_METHOD);
670         methodToCalls.add(KRADConstants.ROUTE_METHOD);
671         methodToCalls.add(KRADConstants.APPROVE_METHOD);
672         methodToCalls.add(KRADConstants.DISAPPROVE_METHOD);
673         return methodToCalls;
674     }
675 
676     /**
677      * This default implementation simply returns false to indicate that custom lock descriptors are not supported by DocumentBase. If custom lock
678      * descriptors are needed, the appropriate subclasses should override this method.
679      * 
680      * @see org.kuali.rice.krad.document.Document#useCustomLockDescriptors()
681      */
682     public boolean useCustomLockDescriptors() {
683     	return false;
684     }
685 
686     /**
687      * This default implementation just throws a PessimisticLockingException. Subclasses of DocumentBase that need support for custom lock descriptors
688      * should override this method.
689      * 
690      * @see org.kuali.rice.krad.document.Document#getCustomLockDescriptor(org.kuali.rice.kim.bo.Person)
691      */
692     public String getCustomLockDescriptor(Person user) {
693     	throw new PessimisticLockingException("Document " + getDocumentNumber() +
694     			" is using pessimistic locking with custom lock descriptors, but the document class has not overriden the getCustomLockDescriptor method");
695     }
696     
697     protected AttachmentService getAttachmentService() {
698 		if ( attachmentService == null ) {
699 			attachmentService = KRADServiceLocator.getAttachmentService();
700 		}
701 		return attachmentService;
702 	}
703     
704     protected NoteService getNoteService() {
705 		if ( noteService == null ) {
706 			noteService = KRADServiceLocator.getNoteService();
707 		}
708 		return noteService;
709 	}
710 }