001/** 002 * Copyright 2005-2015 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.document; 017 018import org.apache.commons.lang.StringUtils; 019import org.apache.log4j.Logger; 020import org.kuali.rice.kew.api.KewApiConstants; 021import org.kuali.rice.kew.api.KewApiServiceLocator; 022import org.kuali.rice.kew.api.action.ActionType; 023import org.kuali.rice.kew.api.exception.WorkflowException; 024import org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent; 025import org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange; 026import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange; 027import org.kuali.rice.kim.api.identity.Person; 028import org.kuali.rice.kim.api.services.KimApiServiceLocator; 029import org.kuali.rice.krad.bo.AdHocRoutePerson; 030import org.kuali.rice.krad.bo.AdHocRouteWorkgroup; 031import org.kuali.rice.krad.bo.DocumentHeader; 032import org.kuali.rice.krad.bo.Note; 033import org.kuali.rice.krad.bo.PersistableBusinessObject; 034import org.kuali.rice.krad.bo.PersistableBusinessObjectBase; 035import org.kuali.rice.krad.datadictionary.DocumentEntry; 036import org.kuali.rice.krad.datadictionary.WorkflowAttributes; 037import org.kuali.rice.krad.datadictionary.WorkflowProperties; 038import org.kuali.rice.krad.document.authorization.PessimisticLock; 039import org.kuali.rice.krad.exception.PessimisticLockingException; 040import org.kuali.rice.krad.exception.ValidationException; 041import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent; 042import org.kuali.rice.krad.service.AttachmentService; 043import org.kuali.rice.krad.service.DocumentSerializerService; 044import org.kuali.rice.krad.service.KRADServiceLocator; 045import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 046import org.kuali.rice.krad.service.NoteService; 047import org.kuali.rice.krad.util.ErrorMessage; 048import org.kuali.rice.krad.util.GlobalVariables; 049import org.kuali.rice.krad.util.KRADConstants; 050import org.kuali.rice.krad.util.KRADPropertyConstants; 051import org.kuali.rice.krad.util.NoteType; 052import org.kuali.rice.krad.util.ObjectUtils; 053import org.kuali.rice.krad.util.documentserializer.AlwaysFalsePropertySerializabilityEvaluator; 054import org.kuali.rice.krad.util.documentserializer.AlwaysTruePropertySerializibilityEvaluator; 055import org.kuali.rice.krad.util.documentserializer.BusinessObjectPropertySerializibilityEvaluator; 056import org.kuali.rice.krad.util.documentserializer.PropertySerializabilityEvaluator; 057import org.kuali.rice.krad.workflow.DocumentInitiator; 058import org.kuali.rice.krad.workflow.KualiDocumentXmlMaterializer; 059import org.kuali.rice.krad.workflow.KualiTransactionalDocumentInformation; 060import org.springframework.util.AutoPopulatingList; 061import org.springframework.util.CollectionUtils; 062 063import javax.persistence.CascadeType; 064import javax.persistence.Column; 065import javax.persistence.FetchType; 066import javax.persistence.Id; 067import javax.persistence.JoinColumn; 068import javax.persistence.MappedSuperclass; 069import javax.persistence.OneToMany; 070import javax.persistence.OneToOne; 071import javax.persistence.Transient; 072import java.util.ArrayList; 073import java.util.Iterator; 074import java.util.List; 075import java.util.Map; 076 077/** 078 * @see Document 079 */ 080@MappedSuperclass 081public abstract class DocumentBase extends PersistableBusinessObjectBase implements Document { 082 private static final Logger LOG = Logger.getLogger(DocumentBase.class); 083 084 @Id @Column(name = "DOC_HDR_ID") 085 protected String documentNumber; 086 @OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinColumn( 087 name = "DOC_HDR_ID", insertable = false, updatable = false) 088 protected DocumentHeader documentHeader; 089 090 @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinColumn( 091 name = "DOC_HDR_ID", insertable = false, updatable = false) 092 private List<PessimisticLock> pessimisticLocks; 093 094 @Transient 095 private List<AdHocRoutePerson> adHocRoutePersons; 096 @Transient 097 private List<AdHocRouteWorkgroup> adHocRouteWorkgroups; 098 @Transient 099 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 = 111 KRADServiceLocatorWeb.getDocumentHeaderService().getDocumentHeaderBaseClass(); 112 setDocumentHeader(documentHeaderClass.newInstance()); 113 pessimisticLocks = new ArrayList<PessimisticLock>(); 114 adHocRoutePersons = new ArrayList<AdHocRoutePerson>(); 115 adHocRouteWorkgroups = new ArrayList<AdHocRouteWorkgroup>(); 116 notes = new ArrayList<Note>(); 117 } catch (IllegalAccessException e) { 118 throw new RuntimeException("Error instantiating DocumentHeader", e); 119 } catch (InstantiationException e) { 120 throw new RuntimeException("Error instantiating DocumentHeader", e); 121 } 122 } 123 124 /** 125 * @see org.kuali.rice.krad.document.Document#getAllowsCopy() 126 */ 127 public boolean getAllowsCopy() { 128 // TODO Auto-generated method stub 129 return false; 130 } 131 132 /** 133 * Retrieves the title of the document 134 * 135 * <p> 136 * This is the default document title implementation. It concatenates the document's data dictionary file label 137 * attribute and 138 * the document's document header description together. This title is used to populate workflow and will show up in 139 * document 140 * search results and user action lists. 141 * </p> 142 * 143 * return String representing the title of the document 144 * 145 * @see org.kuali.rice.krad.document.Document#getDocumentTitle() 146 */ 147 public String getDocumentTitle() { 148 String documentTypeLabel = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName( 149 this.getDocumentHeader().getWorkflowDocument().getDocumentTypeName()).getLabel(); 150 if (null == documentTypeLabel) { 151 documentTypeLabel = ""; 152 } 153 154 String description = this.getDocumentHeader().getDocumentDescription(); 155 if (null == description) { 156 description = ""; 157 } 158 159 return documentTypeLabel + " - " + description; 160 } 161 162 /** 163 * Uses the persistence service's implementation of OJB's retrieveNonKey() fields method. 164 * 165 * @see org.kuali.rice.krad.bo.BusinessObject#refresh() 166 */ 167 @Override 168 public void refresh() { 169 KRADServiceLocator.getPersistenceService().retrieveNonKeyFields(this); 170 } 171 172 /** 173 * Checks to see if the objectId value is empty. If so, it will try to refresh the object from the DB. 174 * 175 * @see org.kuali.rice.krad.document.Document#refreshIfEmpty() 176 */ 177 public void refreshIfEmpty() { 178 if (null == this.getDocumentHeader()) { 179 this.refresh(); 180 } else if (StringUtils.isEmpty(this.getDocumentHeader().getObjectId())) { 181 this.refresh(); 182 } 183 } 184 185 /** 186 * Uses the persistence service to retrieve a reference object of a parent. 187 * 188 * @see org.kuali.rice.krad.document.Document#refreshReferenceObject(java.lang.String) 189 */ 190 @Override 191 public void refreshReferenceObject(String referenceObjectName) { 192 KRADServiceLocator.getPersistenceService().retrieveReferenceObject(this, referenceObjectName); 193 } 194 195 /** 196 * @see org.kuali.rice.krad.document.Document#prepareForSave() 197 */ 198 public void prepareForSave() { 199 // do nothing 200 } 201 202 /** 203 * @see org.kuali.rice.krad.document.Document#processAfterRetrieve() 204 */ 205 public void processAfterRetrieve() { 206 // do nothing 207 } 208 209 /** 210 * The the default implementation for RouteLevelChange does nothing, but is meant to provide a hook for documents to 211 * implement 212 * for other needs. 213 * 214 * @see org.kuali.rice.krad.document.Document#doRouteLevelChange(org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange) 215 */ 216 public void doRouteLevelChange(DocumentRouteLevelChange levelChangeEvent) { 217 // do nothing 218 } 219 220 /** 221 * @see org.kuali.rice.krad.document.Document#doActionTaken(org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent) 222 */ 223 public void doActionTaken(ActionTakenEvent event) { 224 if ((KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry( 225 this.getClass().getName()).getUseWorkflowPessimisticLocking()) && (!getNonLockingActionTakenCodes() 226 .contains(event.getActionTaken().getActionTaken().getCode()))) { 227 KRADServiceLocatorWeb.getPessimisticLockService().establishWorkflowPessimisticLocking(this); 228 } 229 } 230 231 /** 232 * @see org.kuali.rice.krad.document.Document#afterActionTaken(ActionType, org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent) 233 */ 234 public void afterActionTaken(ActionType performed, ActionTakenEvent event) { 235 // do nothing 236 } 237 238 protected List<String> getNonLockingActionTakenCodes() { 239 List<String> actionTakenStatusCodes = new ArrayList<String>(); 240 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_SAVED_CD); 241 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD); 242 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_FYI_CD); 243 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_DENIED_CD); 244 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_CANCELED_CD); 245 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_LOG_DOCUMENT_ACTION_CD); 246 return actionTakenStatusCodes; 247 } 248 249 /** 250 * The the default implementation for afterWorkflowEngineProcess does nothing, but is meant to provide a hook for 251 * documents to implement for other needs. 252 * 253 * @see org.kuali.rice.krad.document.Document#afterWorkflowEngineProcess(boolean) 254 */ 255 public void afterWorkflowEngineProcess(boolean successfullyProcessed) { 256 if (KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry( 257 this.getClass().getName()).getUseWorkflowPessimisticLocking()) { 258 if (successfullyProcessed) { 259 KRADServiceLocatorWeb.getPessimisticLockService().releaseWorkflowPessimisticLocking(this); 260 } 261 } 262 } 263 264 /** 265 * The the default implementation for beforeWorkflowEngineProcess does nothing, but is meant to provide a hook for 266 * documents to implement for other needs. 267 * 268 * @see org.kuali.rice.krad.document.Document#beforeWorkflowEngineProcess() 269 */ 270 public void beforeWorkflowEngineProcess() { 271 // do nothing 272 } 273 274 /** 275 * The default implementation returns no additional ids for the workflow engine to lock prior to processing. 276 * 277 * @see org.kuali.rice.krad.document.Document#getWorkflowEngineDocumentIdsToLock() 278 */ 279 public List<String> getWorkflowEngineDocumentIdsToLock() { 280 return null; 281 } 282 283 /** 284 * @see org.kuali.rice.krad.document.Copyable#toCopy() 285 */ 286 public void toCopy() throws WorkflowException, IllegalStateException { 287 if (!this.getAllowsCopy()) { 288 throw new IllegalStateException(this.getClass().getName() + " does not support document-level copying"); 289 } 290 String sourceDocumentHeaderId = getDocumentNumber(); 291 setNewDocumentHeader(); 292 293 getDocumentHeader().setDocumentTemplateNumber(sourceDocumentHeaderId); 294 295 //clear out notes from previous bo 296 this.notes.clear(); 297 addCopyErrorDocumentNote("copied from document " + sourceDocumentHeaderId); 298 } 299 300 /** 301 * Gets a new document header for this documents type and sets in the document instance. 302 * 303 * @throws WorkflowException 304 */ 305 protected void setNewDocumentHeader() throws WorkflowException { 306 TransactionalDocument newDoc = 307 (TransactionalDocument) KRADServiceLocatorWeb.getDocumentService().getNewDocument( 308 getDocumentHeader().getWorkflowDocument().getDocumentTypeName()); 309 newDoc.getDocumentHeader().setDocumentDescription(getDocumentHeader().getDocumentDescription()); 310 newDoc.getDocumentHeader().setOrganizationDocumentNumber(getDocumentHeader().getOrganizationDocumentNumber()); 311 312 try { 313 ObjectUtils.setObjectPropertyDeep(this, KRADPropertyConstants.DOCUMENT_NUMBER, documentNumber.getClass(), 314 newDoc.getDocumentNumber()); 315 } catch (Exception e) { 316 LOG.error("Unable to set document number property in copied document " + e.getMessage(), e); 317 throw new RuntimeException("Unable to set document number property in copied document " + e.getMessage(), 318 e); 319 } 320 321 // replace current documentHeader with new documentHeader 322 setDocumentHeader(newDoc.getDocumentHeader()); 323 } 324 325 /** 326 * Adds a note to the document indicating it was created by a copy or error correction. 327 * 328 * @param noteText - text for note 329 */ 330 protected void addCopyErrorDocumentNote(String noteText) { 331 Note note = null; 332 try { 333 note = KRADServiceLocatorWeb.getDocumentService().createNoteFromDocument(this, noteText); 334 } catch (Exception e) { 335 logErrors(); 336 throw new RuntimeException("Couldn't create note on copy or error", e); 337 } 338 addNote(note); 339 } 340 341 /** 342 * @see org.kuali.rice.krad.document.Document#getXmlForRouteReport() 343 */ 344 public String getXmlForRouteReport() { 345 prepareForSave(); 346 populateDocumentForRouting(); 347 return getDocumentHeader().getWorkflowDocument().getApplicationContent(); 348 } 349 350 /** 351 * @see org.kuali.rice.krad.document.Document#populateDocumentForRouting() 352 */ 353 public void populateDocumentForRouting() { 354 getDocumentHeader().getWorkflowDocument().setApplicationContent(serializeDocumentToXml()); 355 } 356 357 /** 358 * @see org.kuali.rice.krad.document.Document#serializeDocumentToXml() 359 */ 360 public String serializeDocumentToXml() { 361 DocumentSerializerService documentSerializerService = KRADServiceLocatorWeb.getDocumentSerializerService(); 362 String xml = documentSerializerService.serializeDocumentToXmlForRouting(this); 363 return xml; 364 } 365 366 /** 367 * Wraps a document in an instance of KualiDocumentXmlMaterializer, that provides additional metadata for 368 * serialization 369 * 370 * @see org.kuali.rice.krad.document.Document#wrapDocumentWithMetadataForXmlSerialization() 371 */ 372 public KualiDocumentXmlMaterializer wrapDocumentWithMetadataForXmlSerialization() { 373 KualiTransactionalDocumentInformation transInfo = new KualiTransactionalDocumentInformation(); 374 DocumentInitiator initiator = new DocumentInitiator(); 375 String initiatorPrincipalId = getDocumentHeader().getWorkflowDocument().getDocument().getInitiatorPrincipalId(); 376 Person initiatorUser = KimApiServiceLocator.getPersonService().getPerson(initiatorPrincipalId); 377 initiator.setPerson(initiatorUser); 378 transInfo.setDocumentInitiator(initiator); 379 KualiDocumentXmlMaterializer xmlWrapper = new KualiDocumentXmlMaterializer(); 380 xmlWrapper.setDocument(this); 381 xmlWrapper.setKualiTransactionalDocumentInformation(transInfo); 382 return xmlWrapper; 383 } 384 385 /** 386 * If workflowProperties have been defined within the data dictionary for this document, then it returns an instance 387 * of 388 * {@link BusinessObjectPropertySerializibilityEvaluator} initialized with the properties. If none have been 389 * defined, then returns 390 * {@link AlwaysTruePropertySerializibilityEvaluator}. 391 * 392 * @see org.kuali.rice.krad.document.Document#getDocumentPropertySerizabilityEvaluator() 393 */ 394 public PropertySerializabilityEvaluator getDocumentPropertySerizabilityEvaluator() { 395 String docTypeName = getDocumentHeader().getWorkflowDocument().getDocumentTypeName(); 396 DocumentEntry documentEntry = 397 KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(docTypeName); 398 WorkflowProperties workflowProperties = documentEntry.getWorkflowProperties(); 399 WorkflowAttributes workflowAttributes = documentEntry.getWorkflowAttributes(); 400 return createPropertySerializabilityEvaluator(workflowProperties, workflowAttributes); 401 } 402 403 protected PropertySerializabilityEvaluator createPropertySerializabilityEvaluator( 404 WorkflowProperties workflowProperties, WorkflowAttributes workflowAttributes) { 405 if (workflowAttributes != null) { 406 return new AlwaysFalsePropertySerializabilityEvaluator(); 407 } 408 if (workflowProperties == null) { 409 return new AlwaysTruePropertySerializibilityEvaluator(); 410 } 411 PropertySerializabilityEvaluator evaluator = new BusinessObjectPropertySerializibilityEvaluator(); 412 evaluator.initializeEvaluatorForDocument(this); 413 return evaluator; 414 } 415 416 /** 417 * Returns the POJO property name of "this" document in the object returned by {@link 418 * #wrapDocumentWithMetadataForXmlSerialization()} 419 * 420 * @see org.kuali.rice.krad.document.Document#getBasePathToDocumentDuringSerialization() 421 */ 422 public String getBasePathToDocumentDuringSerialization() { 423 return "document"; 424 } 425 426 /** 427 * @see org.kuali.rice.krad.document.Document#getDocumentHeader() 428 */ 429 public DocumentHeader getDocumentHeader() { 430 return this.documentHeader; 431 } 432 433 /** 434 * @see org.kuali.rice.krad.document.Document#setDocumentHeader(org.kuali.rice.krad.document.DocumentHeader) 435 */ 436 public void setDocumentHeader(DocumentHeader documentHeader) { 437 this.documentHeader = documentHeader; 438 } 439 440 /** 441 * @see org.kuali.rice.krad.document.Document#getDocumentNumber() 442 */ 443 public String getDocumentNumber() { 444 return documentNumber; 445 } 446 447 /** 448 * @see org.kuali.rice.krad.document.Document#setDocumentNumber(java.lang.String) 449 */ 450 public void setDocumentNumber(String documentNumber) { 451 this.documentNumber = documentNumber; 452 } 453 454 /** 455 * @see org.kuali.rice.krad.document.Document#getAdHocRoutePersons() 456 */ 457 public List<AdHocRoutePerson> getAdHocRoutePersons() { 458 return adHocRoutePersons; 459 } 460 461 /** 462 * @see org.kuali.rice.krad.document.Document#setAdHocRoutePersons(java.util.List) 463 */ 464 public void setAdHocRoutePersons(List<AdHocRoutePerson> adHocRoutePersons) { 465 this.adHocRoutePersons = adHocRoutePersons; 466 } 467 468 /** 469 * @see org.kuali.rice.krad.document.Document#getAdHocRouteWorkgroups() 470 */ 471 public List<AdHocRouteWorkgroup> getAdHocRouteWorkgroups() { 472 return adHocRouteWorkgroups; 473 } 474 475 /** 476 * @see org.kuali.rice.krad.document.Document#setAdHocRouteWorkgroups(java.util.List) 477 */ 478 public void setAdHocRouteWorkgroups(List<AdHocRouteWorkgroup> adHocRouteWorkgroups) { 479 this.adHocRouteWorkgroups = adHocRouteWorkgroups; 480 } 481 482 public void postProcessSave(KualiDocumentEvent event) { 483 // TODO Auto-generated method stub 484 485 } 486 487 /** 488 * Override this method with implementation specific prepareForSave logic 489 * 490 * @see org.kuali.rice.krad.document.Document#prepareForSave(org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent) 491 */ 492 public void prepareForSave(KualiDocumentEvent event) { 493 // do nothing by default 494 } 495 496 public void validateBusinessRules(KualiDocumentEvent event) { 497 if (GlobalVariables.getMessageMap().hasErrors()) { 498 logErrors(); 499 throw new ValidationException("errors occured before business rule"); 500 } 501 502 // perform validation against rules engine 503 LOG.info("invoking rules engine on document " + getDocumentNumber()); 504 boolean isValid = true; 505 isValid = KRADServiceLocatorWeb.getKualiRuleService().applyRules(event); 506 507 // check to see if the br eval passed or failed 508 if (!isValid) { 509 logErrors(); 510 // TODO: better error handling at the lower level and a better error message are 511 // needed here 512 throw new ValidationException("business rule evaluation failed"); 513 } else if (GlobalVariables.getMessageMap().hasErrors()) { 514 logErrors(); 515 throw new ValidationException( 516 "Unreported errors occured during business rule evaluation (rule developer needs to put meaningful error messages into global ErrorMap)"); 517 } 518 LOG.debug("validation completed"); 519 520 } 521 522 /** 523 * This method logs errors. 524 */ 525 protected void logErrors() { 526 if (LOG.isInfoEnabled()) { 527 if (GlobalVariables.getMessageMap().hasErrors()) { 528 529 for (Iterator<Map.Entry<String, AutoPopulatingList<ErrorMessage>>> i = 530 GlobalVariables.getMessageMap().getAllPropertiesAndErrors().iterator(); i.hasNext(); ) { 531 Map.Entry<String, AutoPopulatingList<ErrorMessage>> e = i.next(); 532 533 StringBuffer logMessage = new StringBuffer(); 534 logMessage.append("[" + e.getKey() + "] "); 535 boolean first = true; 536 537 AutoPopulatingList<ErrorMessage> errorList = e.getValue(); 538 for (Iterator<ErrorMessage> j = errorList.iterator(); j.hasNext(); ) { 539 ErrorMessage em = j.next(); 540 541 if (first) { 542 first = false; 543 } else { 544 logMessage.append(";"); 545 } 546 logMessage.append(em); 547 } 548 549 LOG.info(logMessage); 550 } 551 } 552 } 553 } 554 555 /** 556 * Hook for override 557 * 558 * @see org.kuali.rice.krad.document.Document#generateSaveEvents() 559 */ 560 public List<KualiDocumentEvent> generateSaveEvents() { 561 return new ArrayList<KualiDocumentEvent>(); 562 } 563 564 /** 565 * @see org.kuali.rice.krad.document.Document#doRouteStatusChange(org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange) 566 */ 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 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 NoteType#BUSINESS_OBJECT} 578 * 579 * @return Returns the documentBusinessObject. 580 */ 581 @Override 582 public PersistableBusinessObject getNoteTarget() { 583 return getDocumentHeader(); 584 } 585 586 /** 587 * Returns the {@link NoteType} to use for notes associated with this document. 588 * By default this returns {@link NoteType#DOCUMENT_HEADER} since notes are 589 * associated with the {@link 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 @Override 657 protected void postLoad() { 658 super.postLoad(); 659 refreshPessimisticLocks(); 660 } 661 662 /** 663 * @see org.kuali.rice.krad.document.Document#getPessimisticLocks() 664 */ 665 public List<PessimisticLock> getPessimisticLocks() { 666 return this.pessimisticLocks; 667 } 668 669 /** 670 * @see org.kuali.rice.krad.document.Document#refreshPessimisticLocks() 671 * @deprecated This is not needed with the relationship set up with JPA annotations 672 */ 673 @Deprecated 674 public void refreshPessimisticLocks() { 675 this.pessimisticLocks.clear(); 676 this.pessimisticLocks = KRADServiceLocatorWeb.getPessimisticLockService().getPessimisticLocksForDocument( 677 this.documentNumber); 678 } 679 680 /** 681 * @param pessimisticLocks the PessimisticLock objects to set 682 */ 683 public void setPessimisticLocks(List<PessimisticLock> pessimisticLocks) { 684 this.pessimisticLocks = pessimisticLocks; 685 } 686 687 /** 688 * @see org.kuali.rice.krad.document.Document#addPessimisticLock(org.kuali.rice.krad.document.authorization.PessimisticLock) 689 */ 690 public void addPessimisticLock(PessimisticLock lock) { 691 this.pessimisticLocks.add(lock); 692 } 693 694 /** 695 * @see org.kuali.rice.krad.document.Document#getLockClearningMethodNames() 696 */ 697 public List<String> getLockClearningMethodNames() { 698 List<String> methodToCalls = new ArrayList<String>(); 699 methodToCalls.add(KRADConstants.CLOSE_METHOD); 700 methodToCalls.add(KRADConstants.CANCEL_METHOD); 701 // methodToCalls.add(RiceConstants.BLANKET_APPROVE_METHOD); 702 methodToCalls.add(KRADConstants.ROUTE_METHOD); 703 methodToCalls.add(KRADConstants.APPROVE_METHOD); 704 methodToCalls.add(KRADConstants.DISAPPROVE_METHOD); 705 methodToCalls.add(KRADConstants.ACKNOWLEDGE_METHOD); 706 return methodToCalls; 707 } 708 709 /** 710 * This default implementation simply returns false to indicate that custom lock descriptors are not supported by 711 * DocumentBase. If custom lock 712 * descriptors are needed, the appropriate subclasses should override this method. 713 * 714 * @see org.kuali.rice.krad.document.Document#useCustomLockDescriptors() 715 */ 716 public boolean useCustomLockDescriptors() { 717 return false; 718 } 719 720 /** 721 * This default implementation just throws a PessimisticLockingException. Subclasses of DocumentBase that need 722 * support for custom lock descriptors 723 * should override this method. 724 * 725 * @see org.kuali.rice.krad.document.Document#getCustomLockDescriptor(org.kuali.rice.kim.api.identity.Person) 726 */ 727 public String getCustomLockDescriptor(Person user) { 728 throw new PessimisticLockingException("Document " + getDocumentNumber() + 729 " is using pessimistic locking with custom lock descriptors, but the document class has not overriden the getCustomLockDescriptor method"); 730 } 731 732 protected AttachmentService getAttachmentService() { 733 if (attachmentService == null) { 734 attachmentService = KRADServiceLocator.getAttachmentService(); 735 } 736 return attachmentService; 737 } 738 739 protected NoteService getNoteService() { 740 if (noteService == null) { 741 noteService = KRADServiceLocator.getNoteService(); 742 } 743 return noteService; 744 } 745}