1 package org.kuali.rice.krad.service.impl;
2
3
4
5
6
7 import org.apache.commons.lang.StringUtils;
8 import org.apache.commons.lang.time.StopWatch;
9 import org.kuali.ole.alert.bo.AlertBo;
10 import org.kuali.ole.alert.bo.AlertEvent;
11 import org.kuali.ole.alert.bo.AlertEventField;
12 import org.kuali.ole.alert.document.OlePersistableBusinessObjectBase;
13 import org.kuali.ole.alert.document.OleTransactionalDocumentBase;
14 import org.kuali.ole.alert.service.AlertGlobalConfigurationServiceImpl;
15 import org.kuali.ole.alert.service.impl.AlertServiceImpl;
16 import org.kuali.ole.deliver.bo.OleDeliverRequestType;
17 import org.kuali.rice.core.api.CoreApiServiceLocator;
18 import org.kuali.rice.core.api.config.ConfigurationException;
19 import org.kuali.rice.core.api.config.property.ConfigurationService;
20 import org.kuali.rice.core.api.datetime.DateTimeService;
21 import org.kuali.rice.core.api.util.RiceKeyConstants;
22 import org.kuali.rice.core.framework.persistence.jta.TransactionalNoValidationExceptionRollback;
23 import org.kuali.rice.kew.api.WorkflowDocument;
24 import org.kuali.rice.kew.api.exception.WorkflowException;
25 import org.kuali.rice.kim.api.identity.Person;
26 import org.kuali.rice.kim.api.identity.PersonService;
27 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
28 import org.kuali.rice.krad.UserSession;
29 import org.kuali.rice.krad.UserSessionUtils;
30 import org.kuali.rice.krad.bo.*;
31 import org.kuali.rice.krad.dao.DocumentDao;
32 import org.kuali.rice.krad.datadictionary.exception.UnknownDocumentTypeException;
33 import org.kuali.rice.krad.document.Document;
34 import org.kuali.rice.krad.document.DocumentAuthorizer;
35 import org.kuali.rice.krad.document.DocumentPresentationController;
36 import org.kuali.rice.krad.exception.DocumentAuthorizationException;
37 import org.kuali.rice.krad.exception.ValidationException;
38 import org.kuali.rice.krad.maintenance.MaintenanceDocument;
39 import org.kuali.rice.krad.maintenance.MaintenanceDocumentBase;
40 import org.kuali.rice.krad.rules.rule.event.*;
41 import org.kuali.rice.krad.service.*;
42 import org.kuali.rice.krad.util.GlobalVariables;
43 import org.kuali.rice.krad.util.KRADConstants;
44 import org.kuali.rice.krad.util.NoteType;
45 import org.kuali.rice.krad.util.ObjectUtils;
46 import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
47 import org.springframework.dao.OptimisticLockingFailureException;
48
49 import java.lang.reflect.Constructor;
50 import java.lang.reflect.InvocationTargetException;
51 import java.text.MessageFormat;
52 import java.util.ArrayList;
53 import java.util.HashMap;
54 import java.util.List;
55 import java.util.Map;
56
57
58
59
60
61
62
63
64
65 @TransactionalNoValidationExceptionRollback
66 public class DocumentServiceImpl implements DocumentService {
67 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentServiceImpl.class);
68
69 private DocumentDao documentDao;
70
71 private DateTimeService dateTimeService;
72 private NoteService noteService;
73 private WorkflowDocumentService workflowDocumentService;
74 private BusinessObjectService businessObjectService;
75 private DataDictionaryService dataDictionaryService;
76 private DocumentHeaderService documentHeaderService;
77 private DocumentDictionaryService documentDictionaryService;
78 private PersonService personService;
79 private ConfigurationService kualiConfigurationService;
80
81 public AlertServiceImpl getAlertService(){
82 return new AlertServiceImpl();
83 }
84
85
86
87
88 @Override
89 public Document saveDocument(Document document) throws WorkflowException, ValidationException {
90 return saveDocument(document, SaveDocumentEvent.class);
91 }
92
93 @Override
94 public Document saveDocument(Document document,
95 Class<? extends KualiDocumentEvent> kualiDocumentEventClass) throws WorkflowException, ValidationException {
96 checkForNulls(document);
97 if (kualiDocumentEventClass == null) {
98 throw new IllegalArgumentException("invalid (null) kualiDocumentEventClass");
99 }
100
101 if (!SaveEvent.class.isAssignableFrom(kualiDocumentEventClass)) {
102 throw new ConfigurationException("The KualiDocumentEvent class '" + kualiDocumentEventClass.getName() +
103 "' does not implement the class '" + SaveEvent.class.getName() + "'");
104 }
105
106
107
108 document.prepareForSave();
109 Document savedDocument = validateAndPersistDocumentAndSaveAdHocRoutingRecipients(document,
110 generateKualiDocumentEvent(document, kualiDocumentEventClass));
111 prepareWorkflowDocument(savedDocument);
112 getWorkflowDocumentService().save(savedDocument.getDocumentHeader().getWorkflowDocument(), null);
113
114 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
115 savedDocument.getDocumentHeader().getWorkflowDocument());
116 if(document instanceof OleTransactionalDocumentBase){
117 OleTransactionalDocumentBase oleTransactionalDocumentBase = (OleTransactionalDocumentBase)document;
118 getAlertService().deleteAlerts(document.getDocumentNumber());
119 getAlertService().saveAlert(oleTransactionalDocumentBase);
120 List<AlertBo> alertBos = oleTransactionalDocumentBase.getAlertBoList();
121 oleTransactionalDocumentBase = (OleTransactionalDocumentBase)savedDocument;
122 oleTransactionalDocumentBase.setAlertBoList(alertBos);
123
124 }
125
126 if(document instanceof MaintenanceDocumentBase){
127 MaintenanceDocumentBase maintenanceDocumentBase = (MaintenanceDocumentBase) document;
128 if(maintenanceDocumentBase.getDocumentDataObject() instanceof OlePersistableBusinessObjectBase){
129 OlePersistableBusinessObjectBase olePersistableBusinessObjectBase = (OlePersistableBusinessObjectBase)maintenanceDocumentBase.getDocumentDataObject();
130 getAlertService().deleteAlerts(maintenanceDocumentBase.getDocumentNumber());
131 getAlertService().deleteAlerts(document.getDocumentNumber());
132 getAlertService().saveAlert(maintenanceDocumentBase);
133
134 }
135 }
136
137 return savedDocument;
138 }
139
140 private KualiDocumentEvent generateKualiDocumentEvent(Document document,
141 Class<? extends KualiDocumentEvent> eventClass) throws ConfigurationException {
142 String potentialErrorMessage =
143 "Found error trying to generate Kuali Document Event using event class '" + eventClass.getName() +
144 "' for document " + document.getDocumentNumber();
145
146 try {
147 Constructor<?> usableConstructor = null;
148 List<Object> paramList = new ArrayList<Object>();
149 for (Constructor<?> currentConstructor : eventClass.getConstructors()) {
150 for (Class<?> parameterClass : currentConstructor.getParameterTypes()) {
151 if (Document.class.isAssignableFrom(parameterClass)) {
152 usableConstructor = currentConstructor;
153 paramList.add(document);
154 } else {
155 paramList.add(null);
156 }
157 }
158 if (ObjectUtils.isNotNull(usableConstructor)) {
159 break;
160 }
161 }
162 if (usableConstructor == null) {
163 throw new RuntimeException("Cannot find a constructor for class '" + eventClass.getName() +
164 "' that takes in a document parameter");
165 }
166 return (KualiDocumentEvent) usableConstructor.newInstance(paramList.toArray());
167 } catch (SecurityException e) {
168 throw new ConfigurationException(potentialErrorMessage, e);
169 } catch (IllegalArgumentException e) {
170 throw new ConfigurationException(potentialErrorMessage, e);
171 } catch (InstantiationException e) {
172 throw new ConfigurationException(potentialErrorMessage, e);
173 } catch (IllegalAccessException e) {
174 throw new ConfigurationException(potentialErrorMessage, e);
175 } catch (InvocationTargetException e) {
176 throw new ConfigurationException(potentialErrorMessage, e);
177 }
178 }
179
180
181
182
183
184 @Override
185 public Document routeDocument(Document document, String annotation,
186 List<AdHocRouteRecipient> adHocRecipients) throws ValidationException, WorkflowException {
187 checkForNulls(document);
188
189
190
191 document.prepareForSave();
192 Document savedDocument = validateAndPersistDocument(document, new RouteDocumentEvent(document));
193 prepareWorkflowDocument(savedDocument);
194 getWorkflowDocumentService()
195 .route(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients);
196 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
197 savedDocument.getDocumentHeader().getWorkflowDocument());
198 removeAdHocPersonsAndWorkgroups(savedDocument);
199 if(document instanceof OleTransactionalDocumentBase){
200 OleTransactionalDocumentBase oleTransactionalDocumentBase = (OleTransactionalDocumentBase)document;
201 getAlertService().deleteAlerts(document.getDocumentNumber());
202 getAlertService().saveAlert(oleTransactionalDocumentBase);
203 List<AlertBo> alertBos = oleTransactionalDocumentBase.getAlertBoList();
204 oleTransactionalDocumentBase = (OleTransactionalDocumentBase)savedDocument;
205 oleTransactionalDocumentBase.setAlertBoList(alertBos);
206 processGlobalAlerts(document);
207 }
208 if(document instanceof MaintenanceDocumentBase){
209 MaintenanceDocumentBase maintenanceDocumentBase = (MaintenanceDocumentBase) document;
210 if(maintenanceDocumentBase.getDocumentDataObject() instanceof OlePersistableBusinessObjectBase){
211 OlePersistableBusinessObjectBase olePersistableBusinessObjectBase = (OlePersistableBusinessObjectBase)maintenanceDocumentBase.getDocumentDataObject();
212 getAlertService().deleteAlerts(maintenanceDocumentBase.getDocumentNumber());
213 getAlertService().deleteAlerts(document.getDocumentNumber());
214 getAlertService().saveAlert(maintenanceDocumentBase);
215 processGlobalAlerts(document);
216 }
217 }
218 return savedDocument;
219 }
220
221
222
223
224
225
226 @Override
227 public Document approveDocument(Document document, String annotation,
228 List<AdHocRouteRecipient> adHocRecipients) throws ValidationException, WorkflowException {
229 checkForNulls(document);
230
231
232
233 document.prepareForSave();
234 Document savedDocument = validateAndPersistDocument(document, new ApproveDocumentEvent(document));
235 prepareWorkflowDocument(savedDocument);
236 getWorkflowDocumentService()
237 .approve(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients);
238 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
239 savedDocument.getDocumentHeader().getWorkflowDocument());
240 removeAdHocPersonsAndWorkgroups(savedDocument);
241 return savedDocument;
242 }
243
244
245
246
247
248 @Override
249 public Document superUserApproveDocument(Document document, String annotation) throws WorkflowException {
250 getDocumentDao().save(document);
251 prepareWorkflowDocument(document);
252 getWorkflowDocumentService().superUserApprove(document.getDocumentHeader().getWorkflowDocument(), annotation);
253 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
254 document.getDocumentHeader().getWorkflowDocument());
255 removeAdHocPersonsAndWorkgroups(document);
256 return document;
257 }
258
259
260
261
262
263 @Override
264 public Document superUserCancelDocument(Document document, String annotation) throws WorkflowException {
265 getDocumentDao().save(document);
266 prepareWorkflowDocument(document);
267 getWorkflowDocumentService().superUserCancel(document.getDocumentHeader().getWorkflowDocument(), annotation);
268 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
269 document.getDocumentHeader().getWorkflowDocument());
270 removeAdHocPersonsAndWorkgroups(document);
271 return document;
272 }
273
274
275
276
277
278 @Override
279 public Document superUserDisapproveDocument(Document document, String annotation) throws WorkflowException {
280 getDocumentDao().save(document);
281 return superUserDisapproveDocumentWithoutSaving(document, annotation);
282 }
283
284
285
286
287
288 @Override
289 public Document superUserDisapproveDocumentWithoutSaving(Document document, String annotation) throws WorkflowException {
290 prepareWorkflowDocument(document);
291 getWorkflowDocumentService()
292 .superUserDisapprove(document.getDocumentHeader().getWorkflowDocument(), annotation);
293 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
294 document.getDocumentHeader().getWorkflowDocument());
295 removeAdHocPersonsAndWorkgroups(document);
296 return document;
297 }
298
299
300
301
302
303
304 @Override
305 public Document disapproveDocument(Document document, String annotation) throws Exception {
306 checkForNulls(document);
307
308 Note note = createNoteFromDocument(document, annotation);
309
310 if (document.getNoteType().equals(NoteType.BUSINESS_OBJECT)) {
311 note.setNoteTypeCode(NoteType.DOCUMENT_HEADER.getCode());
312 note.setRemoteObjectIdentifier(document.getDocumentHeader().getObjectId());
313 }
314 document.addNote(note);
315
316
317
318
319 getNoteService().save(note);
320
321 prepareWorkflowDocument(document);
322 getWorkflowDocumentService().disapprove(document.getDocumentHeader().getWorkflowDocument(), annotation);
323 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
324 document.getDocumentHeader().getWorkflowDocument());
325 removeAdHocPersonsAndWorkgroups(document);
326 return document;
327 }
328
329
330
331
332
333 @Override
334 public Document cancelDocument(Document document, String annotation) throws WorkflowException {
335 checkForNulls(document);
336
337
338
339 if (document instanceof MaintenanceDocument) {
340 MaintenanceDocument maintDoc = ((MaintenanceDocument) document);
341 if (maintDoc.getOldMaintainableObject() != null &&
342 (maintDoc.getOldMaintainableObject().getDataObject() instanceof BusinessObject)) {
343 ((BusinessObject) maintDoc.getOldMaintainableObject().getDataObject()).refresh();
344 }
345
346 if (maintDoc.getNewMaintainableObject().getDataObject() instanceof BusinessObject) {
347 ((BusinessObject) maintDoc.getNewMaintainableObject().getDataObject()).refresh();
348 }
349 }
350 prepareWorkflowDocument(document);
351 getWorkflowDocumentService().cancel(document.getDocumentHeader().getWorkflowDocument(), annotation);
352 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
353 document.getDocumentHeader().getWorkflowDocument());
354
355
356 removeAdHocPersonsAndWorkgroups(document);
357 return document;
358 }
359
360 @Override
361 public Document recallDocument(Document document, String annotation, boolean cancel) throws WorkflowException {
362 checkForNulls(document);
363
364 Note note = createNoteFromDocument(document, annotation);
365 document.addNote(note);
366 getNoteService().save(note);
367
368 prepareWorkflowDocument(document);
369 getWorkflowDocumentService().recall(document.getDocumentHeader().getWorkflowDocument(), annotation, cancel);
370 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
371 document.getDocumentHeader().getWorkflowDocument());
372 removeAdHocPersonsAndWorkgroups(document);
373 return document;
374 }
375
376
377
378
379
380
381 @Override
382 public Document acknowledgeDocument(Document document, String annotation,
383 List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
384 checkForNulls(document);
385
386
387
388 prepareWorkflowDocument(document);
389 getWorkflowDocumentService()
390 .acknowledge(document.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients);
391 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
392 document.getDocumentHeader().getWorkflowDocument());
393 removeAdHocPersonsAndWorkgroups(document);
394 return document;
395 }
396
397
398
399
400
401
402 @Override
403 public Document blanketApproveDocument(Document document, String annotation,
404 List<AdHocRouteRecipient> adHocRecipients) throws ValidationException, WorkflowException {
405 checkForNulls(document);
406
407
408
409 document.prepareForSave();
410 Document savedDocument = validateAndPersistDocument(document, new BlanketApproveDocumentEvent(document));
411 prepareWorkflowDocument(savedDocument);
412 getWorkflowDocumentService()
413 .blanketApprove(savedDocument.getDocumentHeader().getWorkflowDocument(), annotation, adHocRecipients);
414 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
415 savedDocument.getDocumentHeader().getWorkflowDocument());
416 removeAdHocPersonsAndWorkgroups(savedDocument);
417 return savedDocument;
418 }
419
420
421
422
423
424 @Override
425 public Document clearDocumentFyi(Document document,
426 List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
427 checkForNulls(document);
428
429 document.populateDocumentForRouting();
430 getWorkflowDocumentService().clearFyi(document.getDocumentHeader().getWorkflowDocument(), adHocRecipients);
431 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
432 document.getDocumentHeader().getWorkflowDocument());
433 removeAdHocPersonsAndWorkgroups(document);
434 return document;
435 }
436
437
438
439
440
441
442 @Override
443 public Document completeDocument(Document document, String annotation,
444 List adHocRecipients) throws WorkflowException {
445 checkForNulls(document);
446
447 document.prepareForSave();
448 validateAndPersistDocument(document, new CompleteDocumentEvent(document));
449
450 prepareWorkflowDocument(document);
451 getWorkflowDocumentService().complete(document.getDocumentHeader().getWorkflowDocument(), annotation,
452 adHocRecipients);
453
454 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
455 document.getDocumentHeader().getWorkflowDocument());
456
457 removeAdHocPersonsAndWorkgroups(document);
458
459 return document;
460 }
461
462 protected void checkForNulls(Document document) {
463 if (document == null) {
464 throw new IllegalArgumentException("invalid (null) document");
465 }
466 if (document.getDocumentNumber() == null) {
467 throw new IllegalStateException("invalid (null) documentHeaderId");
468 }
469 }
470
471 private Document validateAndPersistDocumentAndSaveAdHocRoutingRecipients(Document document,
472 KualiDocumentEvent event) {
473
474
475
476
477 List<AdHocRouteRecipient> adHocRoutingRecipients = new ArrayList<AdHocRouteRecipient>();
478 adHocRoutingRecipients.addAll(document.getAdHocRoutePersons());
479 adHocRoutingRecipients.addAll(document.getAdHocRouteWorkgroups());
480
481 for (AdHocRouteRecipient recipient : adHocRoutingRecipients) {
482 recipient.setdocumentNumber(document.getDocumentNumber());
483 }
484 Map<String, String> criteria = new HashMap<String, String>();
485 criteria.put("documentNumber", document.getDocumentNumber());
486 getBusinessObjectService().deleteMatching(AdHocRouteRecipient.class, criteria);
487
488 getBusinessObjectService().save(adHocRoutingRecipients);
489 return validateAndPersistDocument(document, event);
490 }
491
492
493
494
495 @Override
496 public boolean documentExists(String documentHeaderId) {
497
498 if (StringUtils.isBlank(documentHeaderId)) {
499 throw new IllegalArgumentException("invalid (blank) documentHeaderId");
500 }
501
502 boolean internalUserSession = false;
503 try {
504
505
506 if (GlobalVariables.getUserSession() == null) {
507 internalUserSession = true;
508 GlobalVariables.setUserSession(new UserSession(KRADConstants.SYSTEM_USER));
509 GlobalVariables.clear();
510 }
511
512
513 if (getWorkflowDocumentService().workflowDocumentExists(documentHeaderId)) {
514
515 return getDocumentHeaderService().getDocumentHeaderById(documentHeaderId) != null;
516 }
517
518 return false;
519 } finally {
520
521 if (internalUserSession) {
522 GlobalVariables.clear();
523 GlobalVariables.setUserSession(null);
524 }
525 }
526 }
527
528
529
530
531
532
533 @Override
534 public Document getNewDocument(Class<? extends Document> documentClass) throws WorkflowException {
535 if (documentClass == null) {
536 throw new IllegalArgumentException("invalid (null) documentClass");
537 }
538 if (!Document.class.isAssignableFrom(documentClass)) {
539 throw new IllegalArgumentException("invalid (non-Document) documentClass");
540 }
541
542 String documentTypeName = getDataDictionaryService().getDocumentTypeNameByClass(documentClass);
543 if (StringUtils.isBlank(documentTypeName)) {
544 throw new UnknownDocumentTypeException(
545 "unable to get documentTypeName for unknown documentClass '" + documentClass.getName() + "'");
546 }
547 return getNewDocument(documentTypeName);
548 }
549
550
551
552
553
554
555
556
557 @Override
558 public Document getNewDocument(String documentTypeName, String initiatorPrincipalNm) throws WorkflowException {
559
560
561 String watchName = "DocumentServiceImpl.getNewDocument";
562 StopWatch watch = new StopWatch();
563 watch.start();
564 if (LOG.isDebugEnabled()) {
565 LOG.debug(watchName + ": started");
566 }
567 if (StringUtils.isBlank(documentTypeName)) {
568 throw new IllegalArgumentException("invalid (blank) documentTypeName");
569 }
570 if (GlobalVariables.getUserSession() == null) {
571 throw new IllegalStateException(
572 "GlobalVariables must be populated with a valid UserSession before a new document can be created");
573 }
574
575
576 Class<? extends Document> documentClass = getDocumentClassByTypeName(documentTypeName);
577
578
579 Person initiator = null;
580 if (StringUtils.isBlank(initiatorPrincipalNm)) {
581 initiator = GlobalVariables.getUserSession().getPerson();
582 } else {
583 initiator = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(initiatorPrincipalNm);
584 if (ObjectUtils.isNull(initiator)) {
585 initiator = GlobalVariables.getUserSession().getPerson();
586 }
587 }
588
589
590 DocumentAuthorizer documentAuthorizer = getDocumentDictionaryService().getDocumentAuthorizer(documentTypeName);
591 DocumentPresentationController documentPresentationController =
592 getDocumentDictionaryService().getDocumentPresentationController(documentTypeName);
593
594 LOG.debug("calling canInitiate from getNewDocument()");
595 if (!documentPresentationController.canInitiate(documentTypeName) ||
596 !documentAuthorizer.canInitiate(documentTypeName, initiator)) {
597 throw new DocumentAuthorizationException(initiator.getPrincipalName(), "initiate", documentTypeName);
598 }
599
600
601 WorkflowDocument workflowDocument = getWorkflowDocumentService().createWorkflowDocument(documentTypeName, initiator);
602 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), workflowDocument);
603
604
605 DocumentHeader documentHeader = null;
606 try {
607
608 Class<? extends DocumentHeader> documentHeaderClass =
609 getDocumentHeaderService().getDocumentHeaderBaseClass();
610 documentHeader = documentHeaderClass.newInstance();
611 documentHeader.setWorkflowDocument(workflowDocument);
612 documentHeader.setDocumentNumber(workflowDocument.getDocumentId());
613
614 } catch (IllegalAccessException e) {
615 throw new RuntimeException("Error instantiating DocumentHeader", e);
616 } catch (InstantiationException e) {
617 throw new RuntimeException("Error instantiating DocumentHeader", e);
618 }
619
620
621 Document document = null;
622 try {
623
624 if (MaintenanceDocumentBase.class.isAssignableFrom(documentClass)) {
625 Class<?>[] defaultConstructor = new Class[]{String.class};
626 Constructor<? extends Document> cons = documentClass.getConstructor(defaultConstructor);
627 if (ObjectUtils.isNull(cons)) {
628 throw new ConfigurationException(
629 "Could not find constructor with document type name parameter needed for Maintenance Document Base class");
630 }
631 document = cons.newInstance(documentTypeName);
632 } else {
633
634 document = documentClass.newInstance();
635 }
636 } catch (IllegalAccessException e) {
637 throw new RuntimeException("Error instantiating Document", e);
638 } catch (InstantiationException e) {
639 throw new RuntimeException("Error instantiating Document", e);
640 } catch (SecurityException e) {
641 throw new RuntimeException("Error instantiating Maintenance Document", e);
642 } catch (NoSuchMethodException e) {
643 throw new RuntimeException(
644 "Error instantiating Maintenance Document: No constructor with String parameter found", e);
645 } catch (IllegalArgumentException e) {
646 throw new RuntimeException("Error instantiating Maintenance Document", e);
647 } catch (InvocationTargetException e) {
648 throw new RuntimeException("Error instantiating Maintenance Document", e);
649 }
650
651 document.setDocumentHeader(documentHeader);
652 document.setDocumentNumber(documentHeader.getDocumentNumber());
653
654 watch.stop();
655 if (LOG.isDebugEnabled()) {
656 LOG.debug(watchName + ": " + watch.toString());
657 }
658
659 return document;
660 }
661
662
663
664
665
666
667 @Override
668 public Document getNewDocument(String documentTypeName) throws WorkflowException {
669 return getNewDocument(documentTypeName, null);
670 }
671
672
673
674
675
676
677
678
679
680
681
682 @Override
683 public Document getByDocumentHeaderId(String documentHeaderId) throws WorkflowException {
684 if (documentHeaderId == null) {
685 throw new IllegalArgumentException("invalid (null) documentHeaderId");
686 }
687 boolean internalUserSession = false;
688 try {
689
690
691 if (GlobalVariables.getUserSession() == null) {
692 internalUserSession = true;
693 GlobalVariables.setUserSession(new UserSession(KRADConstants.SYSTEM_USER));
694 GlobalVariables.clear();
695 }
696
697 WorkflowDocument workflowDocument = null;
698
699 if (LOG.isDebugEnabled()) {
700 LOG.debug("Retrieving doc id: " + documentHeaderId + " from workflow service.");
701 }
702 workflowDocument = getWorkflowDocumentService()
703 .loadWorkflowDocument(documentHeaderId, GlobalVariables.getUserSession().getPerson());
704 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), workflowDocument);
705
706 Class<? extends Document> documentClass = getDocumentClassByTypeName(workflowDocument.getDocumentTypeName());
707
708
709 Document document = getDocumentDao().findByDocumentHeaderId(documentClass, documentHeaderId);
710
711 return postProcessDocument(documentHeaderId, workflowDocument, document);
712 } finally {
713
714 if (internalUserSession) {
715 GlobalVariables.clear();
716 GlobalVariables.setUserSession(null);
717 }
718 }
719 }
720
721
722
723
724 @Override
725 public Document getByDocumentHeaderIdSessionless(String documentHeaderId) throws WorkflowException {
726 if (documentHeaderId == null) {
727 throw new IllegalArgumentException("invalid (null) documentHeaderId");
728 }
729
730 WorkflowDocument workflowDocument = null;
731
732 if (LOG.isDebugEnabled()) {
733 LOG.debug("Retrieving doc id: " + documentHeaderId + " from workflow service.");
734 }
735
736 Person person = getPersonService().getPersonByPrincipalName(KRADConstants.SYSTEM_USER);
737 workflowDocument = workflowDocumentService.loadWorkflowDocument(documentHeaderId, person);
738
739 Class<? extends Document> documentClass = getDocumentClassByTypeName(workflowDocument.getDocumentTypeName());
740
741
742 Document document = getDocumentDao().findByDocumentHeaderId(documentClass, documentHeaderId);
743
744 return postProcessDocument(documentHeaderId, workflowDocument, document);
745 }
746
747 private Class<? extends Document> getDocumentClassByTypeName(String documentTypeName) {
748 if (StringUtils.isBlank(documentTypeName)) {
749 throw new IllegalArgumentException("invalid (blank) documentTypeName");
750 }
751
752 Class<? extends Document> clazz = getDataDictionaryService().getDocumentClassByTypeName(documentTypeName);
753 if (clazz == null) {
754 throw new UnknownDocumentTypeException(
755 "unable to get class for unknown documentTypeName '" + documentTypeName + "'");
756 }
757 return clazz;
758 }
759
760
761
762
763
764
765 protected void loadNotes(Document document) {
766 if (isNoteTargetReady(document)) {
767 List<Note> notes = new ArrayList<Note>();
768 if (StringUtils.isNotBlank(document.getNoteTarget().getObjectId())) {
769 notes.addAll(getNoteService().getByRemoteObjectId(document.getNoteTarget().getObjectId()));
770 }
771
772 if (document.getNoteType().equals(NoteType.BUSINESS_OBJECT)
773 && document.getDocumentHeader().getWorkflowDocument().isDisapproved()) {
774 notes.addAll(getNoteService().getByRemoteObjectId(document.getDocumentHeader().getObjectId()));
775 }
776
777
778
779 for (Note note : notes) {
780 note.refreshReferenceObject("attachment");
781 }
782 document.setNotes(notes);
783 }
784 }
785
786
787
788
789
790
791
792
793 private Document postProcessDocument(String documentHeaderId, WorkflowDocument workflowDocument, Document document) {
794 if (document != null) {
795 document.getDocumentHeader().setWorkflowDocument(workflowDocument);
796 document.processAfterRetrieve();
797 loadNotes(document);
798 }
799 return document;
800 }
801
802
803
804
805
806
807
808 @Override
809 public List<Document> getDocumentsByListOfDocumentHeaderIds(Class<? extends Document> documentClass,
810 List<String> documentHeaderIds) throws WorkflowException {
811
812 if (documentHeaderIds == null) {
813 throw new IllegalArgumentException("invalid (null) documentHeaderId list");
814 }
815 int index = 0;
816 for (String documentHeaderId : documentHeaderIds) {
817 if (StringUtils.isBlank(documentHeaderId)) {
818 throw new IllegalArgumentException("invalid (blank) documentHeaderId at list index " + index);
819 }
820 index++;
821 }
822
823 boolean internalUserSession = false;
824 try {
825
826
827 if (GlobalVariables.getUserSession() == null) {
828 internalUserSession = true;
829 GlobalVariables.setUserSession(new UserSession(KRADConstants.SYSTEM_USER));
830 GlobalVariables.clear();
831 }
832
833
834 List<? extends Document> rawDocuments =
835 getDocumentDao().findByDocumentHeaderIds(documentClass, documentHeaderIds);
836
837
838 List<Document> documents = new ArrayList<Document>();
839 for (Document document : rawDocuments) {
840 WorkflowDocument workflowDocument = getWorkflowDocumentService().loadWorkflowDocument(document.getDocumentNumber(), GlobalVariables.getUserSession().getPerson());
841
842 document = postProcessDocument(document.getDocumentNumber(), workflowDocument, document);
843 documents.add(document);
844 }
845 return documents;
846 } finally {
847
848 if (internalUserSession) {
849 GlobalVariables.clear();
850 GlobalVariables.setUserSession(null);
851 }
852 }
853 }
854
855
856
857
858
859
860 @Override
861 public Document validateAndPersistDocument(Document document, KualiDocumentEvent event) throws ValidationException {
862 if (document == null) {
863 LOG.error("document passed to validateAndPersist was null");
864 throw new IllegalArgumentException("invalid (null) document");
865 }
866 if (LOG.isDebugEnabled()) {
867 LOG.debug("validating and preparing to persist document " + document.getDocumentNumber());
868 }
869
870 document.validateBusinessRules(event);
871 document.prepareForSave(event);
872
873
874 Document savedDocument = null;
875 try {
876 if (LOG.isInfoEnabled()) {
877 LOG.info("storing document " + document.getDocumentNumber());
878 }
879 savedDocument = getDocumentDao().save(document);
880 } catch (OptimisticLockingFailureException e) {
881 LOG.error("exception encountered on store of document " + e.getMessage());
882 throw e;
883 }
884
885 boolean notesSaved = saveDocumentNotes(document);
886 if (!notesSaved) {
887 if (LOG.isInfoEnabled()) {
888 LOG.info(
889 "Notes not saved during validateAndPersistDocument, likely means that note save needs to be deferred because note target is not ready.");
890 }
891 }
892
893 savedDocument.postProcessSave(event);
894
895 return savedDocument;
896 }
897
898
899
900
901
902
903
904 @Override
905 public void prepareWorkflowDocument(Document document) throws WorkflowException {
906
907 document.populateDocumentForRouting();
908
909
910 populateDocumentTitle(document);
911
912
913 populateApplicationDocumentId(document);
914 }
915
916
917
918
919
920
921
922
923
924 private void populateDocumentTitle(Document document) throws WorkflowException {
925 String documentTitle = document.getDocumentTitle();
926 if (StringUtils.isNotBlank(documentTitle)) {
927 document.getDocumentHeader().getWorkflowDocument().setTitle(documentTitle);
928 }
929 }
930
931
932
933
934
935
936
937
938 private void populateApplicationDocumentId(Document document) {
939 String organizationDocumentNumber = document.getDocumentHeader().getOrganizationDocumentNumber();
940 if (StringUtils.isNotBlank(organizationDocumentNumber)) {
941 document.getDocumentHeader().getWorkflowDocument().setApplicationDocumentId(organizationDocumentNumber);
942 }
943 }
944
945
946
947
948
949
950 @Override
951 public Document updateDocument(Document document) {
952 checkForNulls(document);
953 return getDocumentDao().save(document);
954 }
955
956
957
958
959
960 @Override
961 public Note createNoteFromDocument(Document document, String text) {
962 Note note = new Note();
963
964 note.setNotePostedTimestamp(getDateTimeService().getCurrentTimestamp());
965 note.setVersionNumber(Long.valueOf(1));
966 note.setNoteText(text);
967 note.setNoteTypeCode(document.getNoteType().getCode());
968
969 PersistableBusinessObject bo = document.getNoteTarget();
970
971 Person kualiUser = GlobalVariables.getUserSession().getPerson();
972 if (kualiUser == null) {
973 throw new IllegalStateException("Current UserSession has a null Person.");
974 }
975 return bo == null ? null : getNoteService().createNote(note, bo, kualiUser.getPrincipalId());
976 }
977
978
979
980
981 @Override
982 public boolean saveDocumentNotes(Document document) {
983 if (isNoteTargetReady(document)) {
984 List<Note> notes = document.getNotes();
985 for (Note note : document.getNotes()) {
986 linkNoteRemoteObjectId(note, document.getNoteTarget());
987 }
988 getNoteService().saveNoteList(notes);
989 return true;
990 }
991 return false;
992 }
993
994
995
996
997 @Override
998 public void sendNoteRouteNotification(Document document, Note note, Person sender) throws WorkflowException {
999 AdHocRouteRecipient routeRecipient = note.getAdHocRouteRecipient();
1000
1001
1002 Person requestedUser = this.getPersonService().getPersonByPrincipalName(routeRecipient.getId());
1003 String senderName = sender.getFirstName() + " " + sender.getLastName();
1004 String requestedName = requestedUser.getFirstName() + " " + requestedUser.getLastName();
1005
1006 String notificationText =
1007 kualiConfigurationService.getPropertyValueAsString(
1008 RiceKeyConstants.MESSAGE_NOTE_NOTIFICATION_ANNOTATION);
1009 if (StringUtils.isBlank(notificationText)) {
1010 throw new RuntimeException(
1011 "No annotation message found for note notification. Message needs added to application resources with key:" +
1012 RiceKeyConstants.MESSAGE_NOTE_NOTIFICATION_ANNOTATION);
1013 }
1014 notificationText =
1015 MessageFormat.format(notificationText, new Object[]{senderName, requestedName, note.getNoteText()});
1016
1017 List<AdHocRouteRecipient> routeRecipients = new ArrayList<AdHocRouteRecipient>();
1018 routeRecipients.add(routeRecipient);
1019
1020 workflowDocumentService
1021 .sendWorkflowNotification(document.getDocumentHeader().getWorkflowDocument(), notificationText,
1022 routeRecipients, KRADConstants.NOTE_WORKFLOW_NOTIFICATION_REQUEST_LABEL);
1023
1024
1025 note.setAdHocRouteRecipient(new AdHocRoutePerson());
1026 }
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036 protected boolean isNoteTargetReady(Document document) {
1037
1038
1039 if (document.getDocumentHeader().getWorkflowDocument().isDisapproved()) {
1040 return true;
1041 }
1042 PersistableBusinessObject noteTarget = document.getNoteTarget();
1043 if (noteTarget == null || StringUtils.isBlank(noteTarget.getObjectId())) {
1044 return false;
1045 }
1046 return true;
1047 }
1048
1049 private void linkNoteRemoteObjectId(Note note, PersistableBusinessObject noteTarget) {
1050 String objectId = noteTarget.getObjectId();
1051 if (StringUtils.isBlank(objectId)) {
1052 throw new IllegalStateException(
1053 "Attempted to link a Note with a PersistableBusinessObject with no object id");
1054 }
1055 note.setRemoteObjectIdentifier(noteTarget.getObjectId());
1056 }
1057
1058
1059
1060
1061 @Override
1062 public void sendAdHocRequests(Document document, String annotation,
1063 List<AdHocRouteRecipient> adHocRecipients) throws WorkflowException {
1064 prepareWorkflowDocument(document);
1065 getWorkflowDocumentService()
1066 .sendWorkflowNotification(document.getDocumentHeader().getWorkflowDocument(), annotation,
1067 adHocRecipients);
1068 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(),
1069 document.getDocumentHeader().getWorkflowDocument());
1070
1071
1072 removeAdHocPersonsAndWorkgroups(document);
1073 }
1074
1075 private void removeAdHocPersonsAndWorkgroups(Document document) {
1076 List<AdHocRoutePerson> adHocRoutePersons = new ArrayList<AdHocRoutePerson>();
1077 List<AdHocRouteWorkgroup> adHocRouteWorkgroups = new ArrayList<AdHocRouteWorkgroup>();
1078 getBusinessObjectService().delete(document.getAdHocRoutePersons());
1079 getBusinessObjectService().delete(document.getAdHocRouteWorkgroups());
1080 document.setAdHocRoutePersons(adHocRoutePersons);
1081 document.setAdHocRouteWorkgroups(adHocRouteWorkgroups);
1082 }
1083
1084 public void setDateTimeService(DateTimeService dateTimeService) {
1085 this.dateTimeService = dateTimeService;
1086 }
1087
1088 protected DateTimeService getDateTimeService() {
1089 if (this.dateTimeService == null) {
1090 this.dateTimeService = CoreApiServiceLocator.getDateTimeService();
1091 }
1092 return this.dateTimeService;
1093 }
1094
1095 public void setNoteService(NoteService noteService) {
1096 this.noteService = noteService;
1097 }
1098
1099 protected NoteService getNoteService() {
1100 if (this.noteService == null) {
1101 this.noteService = KRADServiceLocator.getNoteService();
1102 }
1103 return this.noteService;
1104 }
1105
1106 public void setBusinessObjectService(BusinessObjectService businessObjectService) {
1107 this.businessObjectService = businessObjectService;
1108 }
1109
1110 protected BusinessObjectService getBusinessObjectService() {
1111 if (this.businessObjectService == null) {
1112 this.businessObjectService = KRADServiceLocator.getBusinessObjectService();
1113 }
1114 return this.businessObjectService;
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 public void setDocumentDao(DocumentDao documentDao) {
1129 this.documentDao = documentDao;
1130 }
1131
1132 protected DocumentDao getDocumentDao() {
1133 return documentDao;
1134 }
1135
1136 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
1137 this.dataDictionaryService = dataDictionaryService;
1138 }
1139
1140 protected DataDictionaryService getDataDictionaryService() {
1141 if (this.dataDictionaryService == null) {
1142 this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
1143 }
1144 return this.dataDictionaryService;
1145 }
1146
1147 public void setDocumentHeaderService(DocumentHeaderService documentHeaderService) {
1148 this.documentHeaderService = documentHeaderService;
1149 }
1150
1151 protected DocumentHeaderService getDocumentHeaderService() {
1152 if (this.documentHeaderService == null) {
1153 this.documentHeaderService = KRADServiceLocatorWeb.getDocumentHeaderService();
1154 }
1155 return this.documentHeaderService;
1156 }
1157
1158 protected DocumentDictionaryService getDocumentDictionaryService() {
1159 if (documentDictionaryService == null) {
1160 documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService();
1161 }
1162 return documentDictionaryService;
1163 }
1164
1165 public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) {
1166 this.documentDictionaryService = documentDictionaryService;
1167 }
1168
1169 public PersonService getPersonService() {
1170 if (personService == null) {
1171 personService = KimApiServiceLocator.getPersonService();
1172 }
1173 return personService;
1174 }
1175
1176 public void setKualiConfigurationService(ConfigurationService kualiConfigurationService) {
1177 this.kualiConfigurationService = kualiConfigurationService;
1178 }
1179
1180
1181 public void processGlobalAlerts(Document document){
1182
1183 AlertGlobalConfigurationServiceImpl alertGlobalConfigurationService = new AlertGlobalConfigurationServiceImpl();
1184 alertGlobalConfigurationService.processAlert(document);
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233 }
1234
1235 }