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