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