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