001package org.kuali.rice.krad.service.impl; 002 003import org.apache.commons.lang.StringUtils; 004import org.apache.commons.lang.time.StopWatch; 005import org.kuali.rice.core.api.CoreApiServiceLocator; 006import org.kuali.rice.core.api.config.ConfigurationException; 007import org.kuali.rice.core.api.config.property.ConfigurationService; 008import org.kuali.rice.core.api.datetime.DateTimeService; 009import org.kuali.rice.core.api.mo.common.GloballyUnique; 010import org.kuali.rice.core.api.util.RiceKeyConstants; 011import org.kuali.rice.core.framework.persistence.jta.TransactionalNoValidationExceptionRollback; 012import org.kuali.rice.kew.api.WorkflowDocument; 013import org.kuali.rice.kew.api.exception.WorkflowException; 014import org.kuali.rice.kim.api.identity.Person; 015import org.kuali.rice.kim.api.identity.PersonService; 016import org.kuali.rice.kim.api.services.KimApiServiceLocator; 017import org.kuali.rice.krad.UserSession; 018import org.kuali.rice.krad.UserSessionUtils; 019import org.kuali.rice.krad.bo.*; 020import org.kuali.rice.krad.datadictionary.exception.UnknownDocumentTypeException; 021import org.kuali.rice.krad.document.Document; 022import org.kuali.rice.krad.document.DocumentAuthorizer; 023import org.kuali.rice.krad.document.DocumentPresentationController; 024import org.kuali.rice.krad.exception.DocumentAuthorizationException; 025import org.kuali.rice.krad.exception.ValidationException; 026import org.kuali.rice.krad.maintenance.Maintainable; 027import org.kuali.rice.krad.maintenance.MaintenanceDocument; 028import org.kuali.rice.krad.maintenance.MaintenanceDocumentBase; 029import org.kuali.rice.krad.rules.rule.event.*; 030import org.kuali.rice.krad.service.*; 031import org.kuali.rice.krad.util.GlobalVariables; 032import org.kuali.rice.krad.util.KRADConstants; 033import org.kuali.rice.krad.util.KRADUtils; 034import org.kuali.rice.krad.util.NoteType; 035import org.kuali.rice.krad.workflow.service.WorkflowDocumentService; 036import org.springframework.beans.factory.annotation.Required; 037import org.springframework.dao.OptimisticLockingFailureException; 038 039import java.lang.reflect.Constructor; 040import java.lang.reflect.InvocationTargetException; 041import java.text.MessageFormat; 042import java.util.ArrayList; 043import java.util.List; 044 045/** 046 * Created by sheiksalahudeenm on 9/23/14. 047 * 048 * Overridden for fixing the 'workflow document is null' issue in KRAD Transaction document with JPA. 049 * Modified method names: saveDocument, superUserApproveDocument, superUserCancelDocument, superUserDisapproveDocument, validateAndPersistDocument and updateDocument 050 * Changes description : saving document header before saving document. Assigning the document header after saving document. 051 */ 052@TransactionalNoValidationExceptionRollback 053public class DocumentServiceImpl implements DocumentService { 054 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentServiceImpl.class); 055 056 protected DateTimeService dateTimeService; 057 protected NoteService noteService; 058 protected WorkflowDocumentService workflowDocumentService; 059 protected LegacyDataAdapter legacyDataAdapter; 060 protected DataDictionaryService dataDictionaryService; 061 protected DocumentDictionaryService documentDictionaryService; 062 protected PersonService personService; 063 protected ConfigurationService kualiConfigurationService; 064 protected DocumentHeaderService documentHeaderService; 065 protected DocumentAdHocService documentAdHocService; 066 067 /** 068 * @see org.kuali.rice.krad.service.DocumentService#saveDocument(org.kuali.rice.krad.document.Document) 069 */ 070 @Override 071 public Document saveDocument(Document document) throws WorkflowException, ValidationException { 072 return saveDocument(document, SaveDocumentEvent.class); 073 } 074 075 /** 076 * saves the document with the custom document event passed in 077 * 078 * {@inheritDoc} 079 */ 080 @Override 081 public Document saveDocument( Document document, DocumentEvent event ) throws WorkflowException { 082 checkForNulls(document); 083 084 if( event == null ) { 085 throw new IllegalArgumentException( "invalid (null) DocumentEvent instance" ); 086 } 087 088 // if event is not an instance of a SaveDocumentEvent or a SaveOnlyDocumentEvent 089 if ( !SaveEvent.class.isAssignableFrom( event.getClass() ) ) { 090 throw new ConfigurationException( "The KualiDocumentEvent class '" + event.getClass().getName() + 091 "' does not implement the class '" + SaveEvent.class.getName() + "'"); 092 } 093 094 document.prepareForSave(); 095 Document savedDocument = validateAndPersistDocumentAndSaveAdHocRoutingRecipients( document, event ); 096 097 prepareWorkflowDocument( savedDocument ); 098 getWorkflowDocumentService().save( savedDocument.getDocumentHeader().getWorkflowDocument(), null ); 099 100 UserSessionUtils.addWorkflowDocument( GlobalVariables.getUserSession(), 101 savedDocument.getDocumentHeader().getWorkflowDocument() ); 102 103 return savedDocument; 104 } 105 106 @Override 107 public Document saveDocument(Document document, 108 Class<? extends DocumentEvent> kualiDocumentEventClass) throws WorkflowException, ValidationException { 109 checkForNulls(document); 110 if (kualiDocumentEventClass == null) { 111 throw new IllegalArgumentException("invalid (null) kualiDocumentEventClass"); 112 } 113 // if event is not an instance of a SaveDocumentEvent or a SaveOnlyDocumentEvent 114 if (!SaveEvent.class.isAssignableFrom(kualiDocumentEventClass)) { 115 throw new ConfigurationException("The KualiDocumentEvent class '" + kualiDocumentEventClass.getName() + 116 "' does not implement the class '" + SaveEvent.class.getName() + "'"); 117 } 118// if (!getDocumentActionFlags(document).getCanSave()) { 119// throw buildAuthorizationException("save", document); 120// } 121 document.prepareForSave(); 122 Document savedDocument = validateAndPersistDocumentAndSaveAdHocRoutingRecipients(document, 123 generateKualiDocumentEvent(document, kualiDocumentEventClass)); 124 /* Modified by 'Sheik Salahudeen' for fixing the 'workflow document is null' issue in KRAD Transaction document with JPA. 125 * Reassign the origional document header to savedocument*/ 126 savedDocument.setDocumentHeader(document.getDocumentHeader()); 127 prepareWorkflowDocument(savedDocument); 128 getWorkflowDocumentService().save(savedDocument.getDocumentHeader().getWorkflowDocument(), null); 129 130 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 131 savedDocument.getDocumentHeader().getWorkflowDocument()); 132 133 return savedDocument; 134 } 135 136 private DocumentEvent generateKualiDocumentEvent(Document document, 137 Class<? extends DocumentEvent> eventClass) throws ConfigurationException { 138 String potentialErrorMessage = 139 "Found error trying to generate Kuali Document Event using event class '" + eventClass.getName() + 140 "' for document " + document.getDocumentNumber(); 141 142 try { 143 Constructor<?> usableConstructor = null; 144 List<Object> paramList = new ArrayList<Object>(); 145 for (Constructor<?> currentConstructor : eventClass.getConstructors()) { 146 for (Class<?> parameterClass : currentConstructor.getParameterTypes()) { 147 if (Document.class.isAssignableFrom(parameterClass)) { 148 usableConstructor = currentConstructor; 149 paramList.add(document); 150 } else { 151 paramList.add(null); 152 } 153 } 154 if (KRADUtils.isNotNull(usableConstructor)) { 155 break; 156 } 157 } 158 if (usableConstructor == null) { 159 throw new RuntimeException("Cannot find a constructor for class '" + eventClass.getName() + 160 "' that takes in a document parameter"); 161 } 162 return (DocumentEvent) usableConstructor.newInstance(paramList.toArray()); 163 } catch (SecurityException e) { 164 throw new ConfigurationException(potentialErrorMessage, e); 165 } catch (IllegalArgumentException e) { 166 throw new ConfigurationException(potentialErrorMessage, e); 167 } catch (InstantiationException e) { 168 throw new ConfigurationException(potentialErrorMessage, e); 169 } catch (IllegalAccessException e) { 170 throw new ConfigurationException(potentialErrorMessage, e); 171 } catch (InvocationTargetException e) { 172 throw new ConfigurationException(potentialErrorMessage, e); 173 } 174 } 175 176 /** 177 * @see org.kuali.rice.krad.service.DocumentService#routeDocument(org.kuali.rice.krad.document.Document, 178 * java.lang.String, java.util.List) 179 */ 180 @Override 181 public Document routeDocument(Document document, String annotation, 182 List<AdHocRouteRecipient> adHocRecipients) throws ValidationException, WorkflowException { 183 checkForNulls(document); 184 //if (!getDocumentActionFlags(document).getCanRoute()) { 185 // throw buildAuthorizationException("route", document); 186 //} 187 document.prepareForSave(); 188 Document savedDocument = validateAndPersistDocument(document, new RouteDocumentEvent(document)); 189 prepareWorkflowDocument(savedDocument); 190 getWorkflowDocumentService() 191 .route(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients); 192 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 193 savedDocument.getDocumentHeader().getWorkflowDocument()); 194 removeAdHocPersonsAndWorkgroups(savedDocument); 195 return savedDocument; 196 } 197 198 /** 199 * @see org.kuali.rice.krad.service.DocumentService#approveDocument(org.kuali.rice.krad.document.Document, 200 * String, 201 * java.util.List) 202 */ 203 @Override 204 public Document approveDocument(Document document, String annotation, 205 List<AdHocRouteRecipient> adHocRecipients) throws ValidationException, WorkflowException { 206 checkForNulls(document); 207 //if (!getDocumentActionFlags(document).getCanApprove()) { 208 // throw buildAuthorizationException("approve", document); 209 //} 210 document.prepareForSave(); 211 Document savedDocument = validateAndPersistDocument(document, new ApproveDocumentEvent(document)); 212 prepareWorkflowDocument(savedDocument); 213 getWorkflowDocumentService() 214 .approve(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients); 215 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 216 savedDocument.getDocumentHeader().getWorkflowDocument()); 217 removeAdHocPersonsAndWorkgroups(savedDocument); 218 return savedDocument; 219 } 220 221 /** 222 * @see org.kuali.rice.krad.service.DocumentService#superUserApproveDocument(org.kuali.rice.krad.document.Document, 223 * String) 224 */ 225 @Override 226 public Document superUserApproveDocument(Document document, String annotation) throws WorkflowException { 227 /* Modified by 'Sheik Salahudeen' for fixing the 'workflow document is null' issue in KRAD Transaction document with JPA. 228 * Saving the document header and Reassign the saved document header */ 229 document.setDocumentHeader(getDocumentHeaderService().saveDocumentHeader(document.getDocumentHeader())); 230 Document savedDocument = getLegacyDataAdapter().saveDocument(document); 231 savedDocument.setDocumentHeader(document.getDocumentHeader()); 232 savedDocument.processAfterRetrieve(); 233 // Need to preserve the workflow document header, which just got left behind 234 savedDocument.getDocumentHeader().setWorkflowDocument(document.getDocumentHeader().getWorkflowDocument()); 235 prepareWorkflowDocument(savedDocument); 236 getWorkflowDocumentService().superUserApprove(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation); 237 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 238 savedDocument.getDocumentHeader().getWorkflowDocument()); 239 removeAdHocPersonsAndWorkgroups(savedDocument); 240 return savedDocument; 241 } 242 243 /** 244 * @see org.kuali.rice.krad.service.DocumentService#superUserCancelDocument(org.kuali.rice.krad.document.Document, 245 * String) 246 */ 247 @Override 248 public Document superUserCancelDocument(Document document, String annotation) throws WorkflowException { 249 /* Modified by 'Sheik Salahudeen' for fixing the 'workflow document is null' issue in KRAD Transaction document with JPA. 250 * Saving the document header and Reassign the saved document header */ 251 document.setDocumentHeader(getDocumentHeaderService().saveDocumentHeader(document.getDocumentHeader())); 252 Document savedDocument = getLegacyDataAdapter().saveDocument(document); 253 savedDocument.setDocumentHeader(document.getDocumentHeader()); 254 savedDocument.processAfterRetrieve(); 255 // Need to preserve the workflow document header, which just got left behind 256 savedDocument.getDocumentHeader().setWorkflowDocument(document.getDocumentHeader().getWorkflowDocument()); 257 prepareWorkflowDocument(savedDocument); 258 getWorkflowDocumentService().superUserCancel(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation); 259 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 260 savedDocument.getDocumentHeader().getWorkflowDocument()); 261 removeAdHocPersonsAndWorkgroups(savedDocument); 262 return savedDocument; 263 } 264 265 /** 266 * @see org.kuali.rice.krad.service.DocumentService#superUserCancelDocument(org.kuali.rice.krad.document.Document, 267 * String) 268 */ 269 @Override 270 public Document superUserDisapproveDocument(Document document, String annotation) throws WorkflowException { 271 /* Modified by 'Sheik Salahudeen' for fixing the 'workflow document is null' issue in KRAD Transaction document with JPA. 272 * Saving the document header and Reassign the saved document header */ 273 document.setDocumentHeader(getDocumentHeaderService().saveDocumentHeader(document.getDocumentHeader())); 274 Document savedDocument = getLegacyDataAdapter().saveDocument(document); 275 savedDocument.setDocumentHeader(document.getDocumentHeader()); 276 savedDocument.processAfterRetrieve(); 277 // Need to preserve the workflow document header, which just got left behind 278 savedDocument.getDocumentHeader().setWorkflowDocument(document.getDocumentHeader().getWorkflowDocument()); 279 return superUserDisapproveDocumentWithoutSaving(savedDocument, annotation); 280 } 281 282 /** 283 * @see org.kuali.rice.krad.service.DocumentService#superUserCancelDocument(org.kuali.rice.krad.document.Document, 284 * String) 285 */ 286 @Override 287 public Document superUserDisapproveDocumentWithoutSaving(Document document, String annotation) throws WorkflowException { 288 prepareWorkflowDocument(document); 289 getWorkflowDocumentService() 290 .superUserDisapprove(document.getDocumentHeader().getWorkflowDocument(), annotation); 291 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 292 document.getDocumentHeader().getWorkflowDocument()); 293 removeAdHocPersonsAndWorkgroups(document); 294 return document; 295 } 296 297 298 /** 299 * @see org.kuali.rice.krad.service.DocumentService#disapproveDocument(org.kuali.rice.krad.document.Document, 300 * String) 301 */ 302 @Override 303 public Document disapproveDocument(Document document, String annotation) throws Exception { 304 checkForNulls(document); 305 306 Note note = createNoteFromDocument(document, annotation); 307 //if note type is BO, override and link disapprove notes to Doc Header 308 if (document.getNoteType().equals(NoteType.BUSINESS_OBJECT)) { 309 note.setNoteTypeCode(NoteType.DOCUMENT_HEADER.getCode()); 310 note.setRemoteObjectIdentifier(document.getDocumentHeader().getObjectId()); 311 } 312 document.addNote(note); 313 314 //SAVE THE NOTE 315 //Note: This save logic is replicated here and in KualiDocumentAction, when to save (based on doc state) should be moved 316 // into a doc service method 317 getNoteService().save(note); 318 319 prepareWorkflowDocument(document); 320 getWorkflowDocumentService().disapprove(document.getDocumentHeader().getWorkflowDocument(), annotation); 321 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 322 document.getDocumentHeader().getWorkflowDocument()); 323 removeAdHocPersonsAndWorkgroups(document); 324 return document; 325 } 326 327 /** 328 * @see org.kuali.rice.krad.service.DocumentService#cancelDocument(org.kuali.rice.krad.document.Document, 329 * String) 330 */ 331 @Override 332 public Document cancelDocument(Document document, String annotation) throws WorkflowException { 333 checkForNulls(document); 334 //if (!getDocumentActionFlags(document).getCanCancel()) { 335 // throw buildAuthorizationException("cancel", document); 336 //} 337 if (document instanceof MaintenanceDocument) { 338 MaintenanceDocument maintDoc = ((MaintenanceDocument) document); 339 if (maintDoc.getOldMaintainableObject() != null && 340 (maintDoc.getOldMaintainableObject().getDataObject() instanceof BusinessObject)) { 341 ((BusinessObject) maintDoc.getOldMaintainableObject().getDataObject()).refresh(); 342 } 343 344 if (maintDoc.getNewMaintainableObject().getDataObject() instanceof BusinessObject) { 345 ((BusinessObject) maintDoc.getNewMaintainableObject().getDataObject()).refresh(); 346 } 347 } 348 prepareWorkflowDocument(document); 349 getWorkflowDocumentService().cancel(document.getDocumentHeader().getWorkflowDocument(), annotation); 350 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 351 document.getDocumentHeader().getWorkflowDocument()); 352 353 removeAdHocPersonsAndWorkgroups(document); 354 return document; 355 } 356 357 @Override 358 public Document recallDocument(Document document, String annotation, boolean cancel) throws WorkflowException { 359 checkForNulls(document); 360 WorkflowDocument workflowDocument = KRADServiceLocatorWeb.getDocumentService(). 361 getByDocumentHeaderId(document.getDocumentNumber()).getDocumentHeader().getWorkflowDocument(); 362 363 if (!workflowDocument.isFinal() && !workflowDocument.isProcessed()) { 364 Note note = createNoteFromDocument(document, annotation); 365 document.addNote(note); 366 getNoteService().save(note); 367 } 368 369 prepareWorkflowDocument(document); 370 getWorkflowDocumentService().recall(document.getDocumentHeader().getWorkflowDocument(), annotation, cancel); 371 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 372 document.getDocumentHeader().getWorkflowDocument()); 373 removeAdHocPersonsAndWorkgroups(document); 374 return document; 375 } 376 377 /** 378 * @see org.kuali.rice.krad.service.DocumentService#acknowledgeDocument(org.kuali.rice.krad.document.Document, 379 * String, 380 * java.util.List) 381 */ 382 @Override 383 public Document acknowledgeDocument(Document document, String annotation, 384 List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException { 385 checkForNulls(document); 386 //if (!getDocumentActionFlags(document).getCanAcknowledge()) { 387 // throw buildAuthorizationException("acknowledge", document); 388 //} 389 prepareWorkflowDocument(document); 390 getWorkflowDocumentService() 391 .acknowledge(document.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients); 392 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 393 document.getDocumentHeader().getWorkflowDocument()); 394 removeAdHocPersonsAndWorkgroups(document); 395 return document; 396 } 397 398 /** 399 * @see org.kuali.rice.krad.service.DocumentService#blanketApproveDocument(org.kuali.rice.krad.document.Document, 400 * String, 401 * java.util.List) 402 */ 403 @Override 404 public Document blanketApproveDocument(Document document, String annotation, 405 List<AdHocRouteRecipient> adHocRecipients) throws ValidationException, WorkflowException { 406 checkForNulls(document); 407 //if (!getDocumentActionFlags(document).getCanBlanketApprove()) { 408 // throw buildAuthorizationException("blanket approve", document); 409 //} 410 document.prepareForSave(); 411 Document savedDocument = validateAndPersistDocument(document, new BlanketApproveDocumentEvent(document)); 412 prepareWorkflowDocument(savedDocument); 413 getWorkflowDocumentService() 414 .blanketApprove(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients); 415 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 416 savedDocument.getDocumentHeader().getWorkflowDocument()); 417 removeAdHocPersonsAndWorkgroups(savedDocument); 418 return savedDocument; 419 } 420 421 /** 422 * @see org.kuali.rice.krad.service.DocumentService#clearDocumentFyi(org.kuali.rice.krad.document.Document, 423 * java.util.List) 424 */ 425 @Override 426 public Document clearDocumentFyi(Document document, 427 List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException { 428 checkForNulls(document); 429 // populate document content so searchable attributes will be indexed properly 430 document.populateDocumentForRouting(); 431 getWorkflowDocumentService().clearFyi(document.getDocumentHeader().getWorkflowDocument(), adHocRecipients); 432 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 433 document.getDocumentHeader().getWorkflowDocument()); 434 removeAdHocPersonsAndWorkgroups(document); 435 return document; 436 } 437 438 /** 439 * @see org.kuali.rice.krad.service.DocumentService#completeDocument(org.kuali.rice.krad.document.Document, 440 * String, 441 * java.util.List) 442 */ 443 @Override 444 public Document completeDocument(Document document, String annotation, 445 List adHocRecipients) throws WorkflowException { 446 checkForNulls(document); 447 448 document.prepareForSave(); 449 Document savedDocument = validateAndPersistDocument(document, new CompleteDocumentEvent(document)); 450 451 prepareWorkflowDocument(savedDocument); 452 getWorkflowDocumentService().complete(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation, 453 adHocRecipients); 454 455 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 456 savedDocument.getDocumentHeader().getWorkflowDocument()); 457 458 removeAdHocPersonsAndWorkgroups(savedDocument); 459 460 return savedDocument; 461 } 462 463 protected void checkForNulls(Document document) { 464 if (document == null) { 465 throw new IllegalArgumentException("invalid (null) document"); 466 } 467 if (document.getDocumentNumber() == null) { 468 throw new IllegalStateException("invalid (null) documentHeaderId"); 469 } 470 } 471 472 private Document validateAndPersistDocumentAndSaveAdHocRoutingRecipients(Document document, 473 DocumentEvent event) { 474 /* 475 * Using this method to wrap validateAndPersistDocument to keep everything in one transaction. This avoids modifying the 476 * signature on validateAndPersistDocument method 477 */ 478 List<AdHocRouteRecipient> adHocRoutingRecipients = new ArrayList<AdHocRouteRecipient>(); 479 adHocRoutingRecipients.addAll(document.getAdHocRoutePersons()); 480 adHocRoutingRecipients.addAll(document.getAdHocRouteWorkgroups()); 481 482 documentAdHocService.replaceAdHocsForDocument( document.getDocumentNumber(), adHocRoutingRecipients ); 483 return validateAndPersistDocument(document, event); 484 } 485 486 /** 487 * @see org.kuali.rice.krad.service.DocumentService#documentExists(String) 488 */ 489 @Override 490 public boolean documentExists(String documentHeaderId) { 491 // validate parameters 492 if (StringUtils.isBlank(documentHeaderId)) { 493 throw new IllegalArgumentException("invalid (blank) documentHeaderId"); 494 } 495 496 boolean internalUserSession = false; 497 try { 498 // KFSMI-2543 - allowed method to run without a user session so it can be used 499 // by workflow processes 500 if (GlobalVariables.getUserSession() == null) { 501 internalUserSession = true; 502 GlobalVariables.setUserSession(new UserSession(KRADConstants.SYSTEM_USER)); 503 GlobalVariables.clear(); 504 } 505 506 // look for workflowDocumentHeader, since that supposedly won't break the transaction 507 if (getWorkflowDocumentService().workflowDocumentExists(documentHeaderId)) { 508 // look for docHeaderId, since that fails without breaking the transaction 509 return documentHeaderService.getDocumentHeaderById(documentHeaderId) != null; 510 } 511 512 return false; 513 } finally { 514 // if a user session was established for this call, clear it our 515 if (internalUserSession) { 516 GlobalVariables.clear(); 517 GlobalVariables.setUserSession(null); 518 } 519 } 520 } 521 522 /** 523 * Creates a new document by class. 524 * 525 * @see org.kuali.rice.krad.service.DocumentService#getNewDocument(Class) 526 */ 527 @Override 528 public Document getNewDocument(Class<? extends Document> documentClass) throws WorkflowException { 529 if (documentClass == null) { 530 throw new IllegalArgumentException("invalid (null) documentClass"); 531 } 532 if (!Document.class.isAssignableFrom(documentClass)) { 533 throw new IllegalArgumentException("invalid (non-Document) documentClass"); 534 } 535 536 String documentTypeName = getDataDictionaryService().getDocumentTypeNameByClass(documentClass); 537 if (StringUtils.isBlank(documentTypeName)) { 538 throw new UnknownDocumentTypeException( 539 "unable to get documentTypeName for unknown documentClass '" + documentClass.getName() + "'"); 540 } 541 return getNewDocument(documentTypeName); 542 } 543 544 /** 545 * Creates a new document by document type name. The principal name 546 * passed in will be used as the document initiator. If the initiatorPrincipalNm 547 * is null or blank, the current user will be used. 548 * 549 * @see org.kuali.rice.krad.service.DocumentService#getNewDocument(String, String) 550 */ 551 @Override 552 public Document getNewDocument(String documentTypeName, String initiatorPrincipalNm) throws WorkflowException { 553 554 // argument validation 555 String watchName = "DocumentServiceImpl.getNewDocument"; 556 StopWatch watch = new StopWatch(); 557 watch.start(); 558 if (LOG.isDebugEnabled()) { 559 LOG.debug(watchName + ": started"); 560 } 561 if (StringUtils.isBlank(documentTypeName)) { 562 throw new IllegalArgumentException("invalid (blank) documentTypeName"); 563 } 564 if (GlobalVariables.getUserSession() == null) { 565 throw new IllegalStateException( 566 "GlobalVariables must be populated with a valid UserSession before a new document can be created"); 567 } 568 569 // get the class for this docTypeName 570 Class<? extends Document> documentClass = getDocumentClassByTypeName(documentTypeName); 571 572 // get the initiator 573 Person initiator = null; 574 if (StringUtils.isBlank(initiatorPrincipalNm)) { 575 initiator = GlobalVariables.getUserSession().getPerson(); 576 } else { 577 initiator = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(initiatorPrincipalNm); 578 if (initiator == null) { 579 initiator = GlobalVariables.getUserSession().getPerson(); 580 } 581 } 582 583 // get the authorization 584 DocumentAuthorizer documentAuthorizer = getDocumentDictionaryService().getDocumentAuthorizer(documentTypeName); 585 DocumentPresentationController documentPresentationController = 586 getDocumentDictionaryService().getDocumentPresentationController(documentTypeName); 587 // make sure this person is authorized to initiate 588 if ( LOG.isDebugEnabled() ) { 589 LOG.debug("calling canInitiate from getNewDocument(" + documentTypeName + "," + initiatorPrincipalNm + ")"); 590 } 591 if (!documentPresentationController.canInitiate(documentTypeName) || 592 !documentAuthorizer.canInitiate(documentTypeName, initiator)) { 593 throw new DocumentAuthorizationException(initiator.getPrincipalName(), "initiate", documentTypeName); 594 } 595 596 // initiate new workflow entry, get the workflow doc 597 WorkflowDocument workflowDocument = getWorkflowDocumentService().createWorkflowDocument(documentTypeName, initiator); 598 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), workflowDocument); 599 600 // create a new document header object 601 DocumentHeader documentHeader = new DocumentHeader(); 602 documentHeader.setWorkflowDocument(workflowDocument); 603 documentHeader.setDocumentNumber(workflowDocument.getDocumentId()); 604 605 // build Document of specified type 606 Document document = null; 607 try { 608 // all maintenance documents have same class 609 if (MaintenanceDocumentBase.class.isAssignableFrom(documentClass)) { 610 Class<?>[] defaultConstructor = new Class[]{String.class}; 611 Constructor<? extends Document> cons = documentClass.getConstructor(defaultConstructor); 612 if (cons == null) { 613 throw new ConfigurationException( 614 "Could not find constructor with document type name parameter needed for Maintenance Document Base class"); 615 } 616 document = cons.newInstance(documentTypeName); 617 } else { 618 // non-maintenance document 619 document = documentClass.newInstance(); 620 } 621 } catch (IllegalAccessException e) { 622 throw new RuntimeException("Error instantiating Document", e); 623 } catch (InstantiationException e) { 624 throw new RuntimeException("Error instantiating Document", e); 625 } catch (SecurityException e) { 626 throw new RuntimeException("Error instantiating Maintenance Document", e); 627 } catch (NoSuchMethodException e) { 628 throw new RuntimeException( 629 "Error instantiating Maintenance Document: No constructor with String parameter found", e); 630 } catch (IllegalArgumentException e) { 631 throw new RuntimeException("Error instantiating Maintenance Document", e); 632 } catch (InvocationTargetException e) { 633 throw new RuntimeException("Error instantiating Maintenance Document", e); 634 } 635 636 document.setDocumentHeader(documentHeader); 637 document.setDocumentNumber(documentHeader.getDocumentNumber()); 638 639 watch.stop(); 640 if (LOG.isDebugEnabled()) { 641 LOG.debug(watchName + ": " + watch.toString()); 642 } 643 644 return document; 645 } 646 647 /** 648 * Creates a new document by document type name. 649 * 650 * @see org.kuali.rice.krad.service.DocumentService#getNewDocument(String) 651 */ 652 @Override 653 public Document getNewDocument(String documentTypeName) throws WorkflowException { 654 return getNewDocument(documentTypeName, null); 655 } 656 657 658 /** 659 * This is temporary until workflow 2.0 and reads from a table to get documents whose status has changed to A 660 * (approved - no 661 * outstanding approval actions requested) 662 * 663 * @param documentHeaderId 664 * @return Document 665 * @throws org.kuali.rice.kew.api.exception.WorkflowException 666 */ 667 @Override 668 public Document getByDocumentHeaderId(String documentHeaderId) throws WorkflowException { 669 if (documentHeaderId == null) { 670 throw new IllegalArgumentException("invalid (null) documentHeaderId"); 671 } 672 boolean internalUserSession = false; 673 try { 674 // KFSMI-2543 - allowed method to run without a user session so it can be used 675 // by workflow processes 676 if (GlobalVariables.getUserSession() == null) { 677 internalUserSession = true; 678 GlobalVariables.setUserSession(new UserSession(KRADConstants.SYSTEM_USER)); 679 GlobalVariables.clear(); 680 } 681 682 WorkflowDocument workflowDocument = null; 683 684 if (LOG.isDebugEnabled()) { 685 LOG.debug("Retrieving doc id: " + documentHeaderId + " from workflow service."); 686 } 687 workflowDocument = getWorkflowDocumentService() 688 .loadWorkflowDocument(documentHeaderId, GlobalVariables.getUserSession().getPerson()); 689 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), workflowDocument); 690 691 Class<? extends Document> documentClass = getDocumentClassByTypeName(workflowDocument.getDocumentTypeName()); 692 693 // retrieve the Document 694 Document document = getLegacyDataAdapter().findByDocumentHeaderId(documentClass, documentHeaderId); 695 return postProcessDocument(documentHeaderId, workflowDocument, document); 696 } finally { 697 // if a user session was established for this call, clear it out 698 if (internalUserSession) { 699 GlobalVariables.clear(); 700 GlobalVariables.setUserSession(null); 701 } 702 } 703 } 704 705 /** 706 * @see org.kuali.rice.krad.service.DocumentService#getByDocumentHeaderIdSessionless(String) 707 */ 708 @Override 709 public Document getByDocumentHeaderIdSessionless(String documentHeaderId) throws WorkflowException { 710 if (documentHeaderId == null) { 711 throw new IllegalArgumentException("invalid (null) documentHeaderId"); 712 } 713 714 WorkflowDocument workflowDocument = null; 715 716 if (LOG.isDebugEnabled()) { 717 LOG.debug("Retrieving doc id: " + documentHeaderId + " from workflow service."); 718 } 719 720 Person person = getPersonService().getPersonByPrincipalName(KRADConstants.SYSTEM_USER); 721 workflowDocument = workflowDocumentService.loadWorkflowDocument(documentHeaderId, person); 722 723 Class<? extends Document> documentClass = getDocumentClassByTypeName(workflowDocument.getDocumentTypeName()); 724 725 // retrieve the Document 726 Document document = getLegacyDataAdapter().findByDocumentHeaderId(documentClass, documentHeaderId); 727 728 return postProcessDocument(documentHeaderId, workflowDocument, document); 729 } 730 731 private Class<? extends Document> getDocumentClassByTypeName(String documentTypeName) { 732 if (StringUtils.isBlank(documentTypeName)) { 733 throw new IllegalArgumentException("invalid (blank) documentTypeName"); 734 } 735 736 Class<? extends Document> clazz = getDataDictionaryService().getDocumentClassByTypeName(documentTypeName); 737 if (clazz == null) { 738 throw new UnknownDocumentTypeException( 739 "unable to get class for unknown documentTypeName '" + documentTypeName + "'"); 740 } 741 return clazz; 742 } 743 744 /** 745 * Loads the Notes for the note target on this Document. 746 * 747 * @param document the document for which to load the notes 748 */ 749 protected void loadNotes(final Document document) { 750 if (isNoteTargetReady(document)) { 751 Object legacyObjectClass; 752 if (document instanceof MaintenanceDocument) { 753 MaintenanceDocument mdoc = (MaintenanceDocument) document; 754 legacyObjectClass = ((Maintainable) org.apache.commons.lang.ObjectUtils.defaultIfNull(mdoc.getOldMaintainableObject(), mdoc.getNewMaintainableObject())).getDataObjectClass(); 755 } else { 756 legacyObjectClass = document.getClass(); 757 } 758 759 List<Note> notes = new ArrayList<Note>(); 760 if (StringUtils.isNotBlank(document.getNoteTarget().getObjectId())) { 761 notes.addAll(getNoteService().getByRemoteObjectId(document.getNoteTarget().getObjectId())); 762 } 763 //notes created on 'disapprove' are linked to Doc Header, so this checks that even if notetype = BO 764 if (document.getNoteType().equals(NoteType.BUSINESS_OBJECT) && document.getDocumentHeader() 765 .getWorkflowDocument().isDisapproved()) { 766 notes.addAll(getNoteService().getByRemoteObjectId(document.getDocumentHeader().getObjectId())); 767 } 768 769 document.setNotes(notes); 770 } 771 } 772 773 /** 774 * Performs required post-processing for every document from the documentDao 775 * 776 * @param documentHeaderId 777 * @param workflowDocument 778 * @param document 779 */ 780 private Document postProcessDocument(String documentHeaderId, WorkflowDocument workflowDocument, Document document) { 781 if (document != null) { 782 document.getDocumentHeader().setWorkflowDocument(workflowDocument); 783 document.processAfterRetrieve(); 784 loadNotes(document); 785 } 786 return document; 787 } 788 789 /** 790 * The default implementation - this retrieves all documents by a list of documentHeader for a given class. 791 * 792 * @see org.kuali.rice.krad.service.DocumentService#getDocumentsByListOfDocumentHeaderIds(Class, 793 * java.util.List) 794 */ 795 @Override 796 public List<Document> getDocumentsByListOfDocumentHeaderIds(Class<? extends Document> documentClass, 797 List<String> documentHeaderIds) throws WorkflowException { 798 // validate documentHeaderIdList and contents 799 if (documentHeaderIds == null) { 800 throw new IllegalArgumentException("invalid (null) documentHeaderId list"); 801 } 802 int index = 0; 803 for (String documentHeaderId : documentHeaderIds) { 804 if (StringUtils.isBlank(documentHeaderId)) { 805 throw new IllegalArgumentException("invalid (blank) documentHeaderId at list index " + index); 806 } 807 index++; 808 } 809 810 boolean internalUserSession = false; 811 try { 812 // KFSMI-2543 - allowed method to run without a user session so it can be used 813 // by workflow processes 814 if (GlobalVariables.getUserSession() == null) { 815 internalUserSession = true; 816 GlobalVariables.setUserSession(new UserSession(KRADConstants.SYSTEM_USER)); 817 GlobalVariables.clear(); 818 } 819 820 // retrieve all documents that match the document header ids 821 List<? extends Document> rawDocuments = getLegacyDataAdapter().findByDocumentHeaderIds(documentClass, 822 documentHeaderIds); 823 824 // post-process them 825 List<Document> documents = new ArrayList<Document>(); 826 for (Document document : rawDocuments) { 827 WorkflowDocument workflowDocument = getWorkflowDocumentService().loadWorkflowDocument(document.getDocumentNumber(), GlobalVariables.getUserSession().getPerson()); 828 829 document = postProcessDocument(document.getDocumentNumber(), workflowDocument, document); 830 documents.add(document); 831 } 832 return documents; 833 } finally { 834 // if a user session was established for this call, clear it our 835 if (internalUserSession) { 836 GlobalVariables.clear(); 837 GlobalVariables.setUserSession(null); 838 } 839 } 840 } 841 842 /* Helper Methods */ 843 844 /** 845 * Validates and persists a document. 846 */ 847 @Override 848 public Document validateAndPersistDocument(Document document, DocumentEvent event) throws ValidationException { 849 if (document == null) { 850 LOG.error("document passed to validateAndPersist was null"); 851 throw new IllegalArgumentException("invalid (null) document"); 852 } 853 if (LOG.isDebugEnabled()) { 854 LOG.debug("validating and preparing to persist document " + document.getDocumentNumber()); 855 } 856 857 document.validateBusinessRules(event); 858 document.prepareForSave(event); 859 860 // save the document 861 Document savedDocument = null; 862 try { 863 if (LOG.isInfoEnabled()) { 864 LOG.info("storing document " + document.getDocumentNumber()); 865 } 866 /* Modified by 'Sheik Salahudeen' for fixing the 'workflow document is null' issue in KRAD Transaction document with JPA. 867 * Saving the document header and Reassign the saved document header */ 868 869 870 document.setDocumentHeader(getDocumentHeaderService().saveDocumentHeader(document.getDocumentHeader())); 871 savedDocument = getLegacyDataAdapter().saveDocument(document); 872 savedDocument.setDocumentHeader(document.getDocumentHeader()); 873 savedDocument.processAfterRetrieve(); 874 // Need to preserve the workflow document header, which just got left behind 875 savedDocument.getDocumentHeader().setWorkflowDocument(document.getDocumentHeader().getWorkflowDocument()); 876 } catch (OptimisticLockingFailureException e) { 877 LOG.error("exception encountered on store of document " + e.getMessage()); 878 throw e; 879 } 880 881 boolean notesSaved = saveDocumentNotes(savedDocument); 882 if (!notesSaved) { 883 if (LOG.isInfoEnabled()) { 884 LOG.info( 885 "Notes not saved during validateAndPersistDocument, likely means that note save needs to be deferred because note target is not ready."); 886 } 887 } 888 889 savedDocument.postProcessSave(event); 890 891 return savedDocument; 892 } 893 894 /** 895 * Sets the title and app document id in the flex document 896 * 897 * @param document 898 * @throws org.kuali.rice.kew.api.exception.WorkflowException 899 */ 900 @Override 901 public void prepareWorkflowDocument(Document document) throws WorkflowException { 902 // populate document content so searchable attributes will be indexed properly 903 document.populateDocumentForRouting(); 904 905 // make sure we push the document title into the workflowDocument 906 populateDocumentTitle(document); 907 908 // make sure we push the application document id into the workflowDocument 909 populateApplicationDocumentId(document); 910 } 911 912 /** 913 * This method will grab the generated document title from the document and add it to the workflowDocument so that 914 * it gets pushed into 915 * workflow when routed. 916 * 917 * @param document 918 * @throws org.kuali.rice.kew.api.exception.WorkflowException 919 */ 920 private void populateDocumentTitle(Document document) throws WorkflowException { 921 String documentTitle = document.getDocumentTitle(); 922 if (StringUtils.isNotBlank(documentTitle)) { 923 document.getDocumentHeader().getWorkflowDocument().setTitle(documentTitle); 924 } 925 } 926 927 /** 928 * This method will grab the organization document number from the document and add it to the workflowDocument so 929 * that it gets pushed 930 * into workflow when routed. 931 * 932 * @param document 933 */ 934 private void populateApplicationDocumentId(Document document) { 935 String organizationDocumentNumber = document.getDocumentHeader().getOrganizationDocumentNumber(); 936 if (StringUtils.isNotBlank(organizationDocumentNumber)) { 937 document.getDocumentHeader().getWorkflowDocument().setApplicationDocumentId(organizationDocumentNumber); 938 } 939 } 940 941 /** 942 * This is to allow for updates of document statuses and other related requirements for updates outside of the 943 * initial save and 944 * route 945 */ 946 @Override 947 public Document updateDocument(Document document) { 948 checkForNulls(document); 949 /* Modified by 'Sheik Salahudeen' for fixing the 'workflow document is null' issue in KRAD Transaction document with JPA. 950 * Saving the document header and Reassign the saved document header */ 951 document.setDocumentHeader(getDocumentHeaderService().saveDocumentHeader(document.getDocumentHeader())); 952 Document savedDocument = getLegacyDataAdapter().saveDocument(document); 953 savedDocument.setDocumentHeader(document.getDocumentHeader()); 954 savedDocument.processAfterRetrieve(); 955 // Need to preserve the workflow document header, which just got left behind 956 savedDocument.getDocumentHeader().setWorkflowDocument(document.getDocumentHeader().getWorkflowDocument()); 957 return savedDocument; 958 } 959 960 /** 961 * @see org.kuali.rice.krad.service.DocumentService#createNoteFromDocument(org.kuali.rice.krad.document.Document, 962 * String) 963 */ 964 @Override 965 public Note createNoteFromDocument(Document document, String text) { 966 Note note = new Note(); 967 968 note.setNotePostedTimestamp(getDateTimeService().getCurrentTimestamp()); 969 note.setNoteText(text); 970 note.setNoteTypeCode(document.getNoteType().getCode()); 971 972 GloballyUnique bo = document.getNoteTarget(); 973 // TODO gah! this is awful 974 Person kualiUser = GlobalVariables.getUserSession().getPerson(); 975 if (kualiUser == null) { 976 throw new IllegalStateException("Current UserSession has a null Person."); 977 } 978 return bo == null ? null : getNoteService().createNote(note, bo, kualiUser.getPrincipalId()); 979 } 980 981 /** 982 * @see org.kuali.rice.krad.service.DocumentService#saveDocumentNotes(org.kuali.rice.krad.document.Document) 983 */ 984 @Override 985 public boolean saveDocumentNotes(Document document) { 986 if (isNoteTargetReady(document)) { 987 List<Note> notes = document.getNotes(); 988 for (Note note : document.getNotes()) { 989 linkNoteRemoteObjectId(note, document.getNoteTarget()); 990 } 991 getNoteService().saveNoteList(notes); 992 return true; 993 } 994 return false; 995 } 996 997 /** 998 * @see org.kuali.rice.krad.service.DocumentService 999 */ 1000 @Override 1001 public Document sendNoteRouteNotification(Document document, Note note, Person sender) throws WorkflowException { 1002 AdHocRouteRecipient routeRecipient = note.getAdHocRouteRecipient(); 1003 1004 // build notification request 1005 Person requestedUser = this.getPersonService().getPersonByPrincipalName(routeRecipient.getId()); 1006 String senderName = sender.getFirstName() + " " + sender.getLastName(); 1007 String requestedName = requestedUser.getFirstName() + " " + requestedUser.getLastName(); 1008 1009 String notificationText = 1010 kualiConfigurationService.getPropertyValueAsString( 1011 RiceKeyConstants.MESSAGE_NOTE_NOTIFICATION_ANNOTATION); 1012 if (StringUtils.isBlank(notificationText)) { 1013 throw new RuntimeException( 1014 "No annotation message found for note notification. Message needs added to application resources with key:" + 1015 RiceKeyConstants.MESSAGE_NOTE_NOTIFICATION_ANNOTATION); 1016 } 1017 notificationText = 1018 MessageFormat.format(notificationText, new Object[]{senderName, requestedName, note.getNoteText()}); 1019 1020 List<AdHocRouteRecipient> routeRecipients = new ArrayList<AdHocRouteRecipient>(); 1021 routeRecipients.add(routeRecipient); 1022 1023 workflowDocumentService 1024 .sendWorkflowNotification(document.getDocumentHeader().getWorkflowDocument(), notificationText, 1025 routeRecipients, KRADConstants.NOTE_WORKFLOW_NOTIFICATION_REQUEST_LABEL); 1026 1027 // clear recipient allowing an notification to be sent to another person 1028 note.setAdHocRouteRecipient(new AdHocRoutePerson()); 1029 return document; 1030 } 1031 1032 /** 1033 * Determines if the given document's note target is ready for notes to be 1034 * attached and persisted against it. This method verifies that the document's 1035 * note target is non-null as well as checking that it has a non-empty object id. 1036 * 1037 * @param document the document on which to check for note target readiness 1038 * @return true if the note target is ready, false otherwise 1039 */ 1040 protected boolean isNoteTargetReady(Document document) { 1041 1042 //special case for disappoved documents 1043 if (document.getDocumentHeader().getWorkflowDocument().isDisapproved()) { 1044 return true; 1045 } 1046 GloballyUnique noteTarget = document.getNoteTarget(); 1047 if (noteTarget == null || StringUtils.isBlank(noteTarget.getObjectId())) { 1048 return false; 1049 } 1050 return true; 1051 } 1052 1053 private void linkNoteRemoteObjectId(Note note, GloballyUnique noteTarget) { 1054 String objectId = noteTarget.getObjectId(); 1055 if (StringUtils.isBlank(objectId)) { 1056 throw new IllegalStateException( 1057 "Attempted to link a Note with a PersistableBusinessObject with no object id"); 1058 } 1059 note.setRemoteObjectIdentifier(noteTarget.getObjectId()); 1060 } 1061 1062 /** 1063 * @see org.kuali.rice.krad.service.DocumentService#sendAdHocRequests(org.kuali.rice.krad.document.Document, String, java.util.List) 1064 */ 1065 @Override 1066 public Document sendAdHocRequests(Document document, String annotation, 1067 List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException { 1068 prepareWorkflowDocument(document); 1069 getWorkflowDocumentService() 1070 .sendWorkflowNotification(document.getDocumentHeader().getWorkflowDocument(), annotation, 1071 adHocRecipients); 1072 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), 1073 document.getDocumentHeader().getWorkflowDocument()); 1074 1075 removeAdHocPersonsAndWorkgroups(document); 1076 return document; 1077 } 1078 1079 private void removeAdHocPersonsAndWorkgroups(Document document) { 1080 documentAdHocService.replaceAdHocsForDocument(document.getDocumentNumber(), null); 1081 document.setAdHocRoutePersons(new ArrayList<AdHocRoutePerson>()); 1082 document.setAdHocRouteWorkgroups(new ArrayList<AdHocRouteWorkgroup>()); 1083 } 1084 1085 @Required 1086 public void setDateTimeService(DateTimeService dateTimeService) { 1087 this.dateTimeService = dateTimeService; 1088 } 1089 1090 protected DateTimeService getDateTimeService() { 1091 if (this.dateTimeService == null) { 1092 this.dateTimeService = CoreApiServiceLocator.getDateTimeService(); 1093 } 1094 return this.dateTimeService; 1095 } 1096 1097 @Required 1098 public void setNoteService(NoteService noteService) { 1099 this.noteService = noteService; 1100 } 1101 1102 protected NoteService getNoteService() { 1103 if (this.noteService == null) { 1104 this.noteService = KRADServiceLocator.getNoteService(); 1105 } 1106 return this.noteService; 1107 } 1108 1109 public void setLegacyDataAdapter(LegacyDataAdapter legacyDataAdapter) { 1110 this.legacyDataAdapter = legacyDataAdapter; 1111 } 1112 1113 protected LegacyDataAdapter getLegacyDataAdapter() { 1114 return this.legacyDataAdapter; 1115 } 1116 1117 public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) { 1118 this.workflowDocumentService = workflowDocumentService; 1119 } 1120 1121 protected WorkflowDocumentService getWorkflowDocumentService() { 1122 if (this.workflowDocumentService == null) { 1123 this.workflowDocumentService = KRADServiceLocatorWeb.getWorkflowDocumentService(); 1124 } 1125 return this.workflowDocumentService; 1126 } 1127 1128 @Required 1129 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 1130 this.dataDictionaryService = dataDictionaryService; 1131 } 1132 1133 protected DataDictionaryService getDataDictionaryService() { 1134 if (this.dataDictionaryService == null) { 1135 this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 1136 } 1137 return this.dataDictionaryService; 1138 } 1139 1140 protected DocumentDictionaryService getDocumentDictionaryService() { 1141 if (documentDictionaryService == null) { 1142 documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService(); 1143 } 1144 return documentDictionaryService; 1145 } 1146 1147 @Required 1148 public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) { 1149 this.documentDictionaryService = documentDictionaryService; 1150 } 1151 1152 public PersonService getPersonService() { 1153 if (personService == null) { 1154 personService = KimApiServiceLocator.getPersonService(); 1155 } 1156 return personService; 1157 } 1158 1159 @Required 1160 public void setKualiConfigurationService(ConfigurationService kualiConfigurationService) { 1161 this.kualiConfigurationService = kualiConfigurationService; 1162 } 1163 1164 public DocumentHeaderService getDocumentHeaderService() { 1165 return documentHeaderService; 1166 } 1167 1168 @Required 1169 public void setDocumentHeaderService(DocumentHeaderService documentHeaderService) { 1170 this.documentHeaderService = documentHeaderService; 1171 } 1172 1173 @Required 1174 public void setDocumentAdHocService(DocumentAdHocService documentAdHocService) { 1175 this.documentAdHocService = documentAdHocService; 1176 } 1177 1178}