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