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