1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.document;
17
18 import java.util.ArrayList;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Map;
22
23 import javax.persistence.Column;
24 import javax.persistence.Id;
25 import javax.persistence.MappedSuperclass;
26 import javax.persistence.PostLoad;
27 import javax.persistence.PostRemove;
28 import javax.persistence.PrePersist;
29 import javax.persistence.Transient;
30
31 import org.apache.commons.lang.StringUtils;
32 import org.apache.log4j.Logger;
33 import org.kuali.rice.core.api.mo.common.GloballyUnique;
34 import org.kuali.rice.kew.api.KewApiConstants;
35 import org.kuali.rice.kew.api.KewApiServiceLocator;
36 import org.kuali.rice.kew.api.WorkflowDocument;
37 import org.kuali.rice.kew.api.action.ActionRequest;
38 import org.kuali.rice.kew.api.action.ActionType;
39 import org.kuali.rice.kew.api.exception.WorkflowException;
40 import org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent;
41 import org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange;
42 import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
43 import org.kuali.rice.kim.api.identity.Person;
44 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
45 import org.kuali.rice.krad.UserSessionUtils;
46 import org.kuali.rice.krad.bo.AdHocRoutePerson;
47 import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
48 import org.kuali.rice.krad.bo.DocumentHeader;
49 import org.kuali.rice.krad.bo.Note;
50 import org.kuali.rice.krad.bo.PersistableBusinessObjectBaseAdapter;
51 import org.kuali.rice.krad.datadictionary.DocumentEntry;
52 import org.kuali.rice.krad.datadictionary.WorkflowAttributes;
53 import org.kuali.rice.krad.datadictionary.WorkflowProperties;
54 import org.kuali.rice.krad.document.authorization.PessimisticLock;
55 import org.kuali.rice.krad.exception.PessimisticLockingException;
56 import org.kuali.rice.krad.exception.ValidationException;
57 import org.kuali.rice.krad.rules.rule.event.DocumentEvent;
58 import org.kuali.rice.krad.service.AttachmentService;
59 import org.kuali.rice.krad.service.DocumentSerializerService;
60 import org.kuali.rice.krad.service.KRADServiceLocator;
61 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
62 import org.kuali.rice.krad.service.NoteService;
63 import org.kuali.rice.krad.util.ErrorMessage;
64 import org.kuali.rice.krad.util.GlobalVariables;
65 import org.kuali.rice.krad.util.KRADConstants;
66 import org.kuali.rice.krad.util.KRADPropertyConstants;
67 import org.kuali.rice.krad.util.NoteType;
68 import org.kuali.rice.krad.util.documentserializer.AlwaysFalsePropertySerializabilityEvaluator;
69 import org.kuali.rice.krad.util.documentserializer.AlwaysTruePropertySerializibilityEvaluator;
70 import org.kuali.rice.krad.util.documentserializer.BusinessObjectPropertySerializibilityEvaluator;
71 import org.kuali.rice.krad.util.documentserializer.PropertySerializabilityEvaluator;
72 import org.kuali.rice.krad.workflow.DocumentInitiator;
73 import org.kuali.rice.krad.workflow.KualiDocumentXmlMaterializer;
74 import org.kuali.rice.krad.workflow.KualiTransactionalDocumentInformation;
75 import org.springframework.util.CollectionUtils;
76
77
78
79
80 @MappedSuperclass
81 public abstract class DocumentBase extends PersistableBusinessObjectBaseAdapter implements Document {
82 private static final long serialVersionUID = 8530945307802647664L;
83 private static final Logger LOG = Logger.getLogger(DocumentBase.class);
84
85 @Id
86 @Column(name = "DOC_HDR_ID",length=14)
87 protected String documentNumber;
88
89 @Transient
90 protected DocumentHeader documentHeader;
91
92 @Transient
93 protected List<PessimisticLock> pessimisticLocks;
94
95 @Transient
96 protected List<AdHocRoutePerson> adHocRoutePersons;
97 @Transient
98 protected List<AdHocRouteWorkgroup> adHocRouteWorkgroups;
99 @Transient
100 protected List<Note> notes;
101 @Transient
102 private String superUserAnnotation = "";
103
104 private transient NoteService noteService;
105 private transient AttachmentService attachmentService;
106
107
108
109
110 public DocumentBase() {
111 documentHeader = new DocumentHeader();
112 pessimisticLocks = new ArrayList<PessimisticLock>();
113 adHocRoutePersons = new ArrayList<AdHocRoutePerson>();
114 adHocRouteWorkgroups = new ArrayList<AdHocRouteWorkgroup>();
115 notes = new ArrayList<Note>();
116 }
117
118
119
120
121 @Override
122 public boolean getAllowsCopy() {
123 return false;
124 }
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 @Override
142 public String getDocumentTitle() {
143 String documentTypeLabel = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeByName(
144 this.getDocumentHeader().getWorkflowDocument().getDocumentTypeName()).getLabel();
145 if (null == documentTypeLabel) {
146 documentTypeLabel = "";
147 }
148
149 String description = this.getDocumentHeader().getDocumentDescription();
150 if (null == description) {
151 description = "";
152 }
153
154 return documentTypeLabel + " - " + description;
155 }
156
157
158
159
160 @Override
161 public void prepareForSave() {
162
163 }
164
165
166
167
168 @Override
169 public void processAfterRetrieve() {
170
171 }
172
173
174
175
176
177
178
179
180 @Override
181 public void doRouteLevelChange(DocumentRouteLevelChange levelChangeEvent) {
182
183 }
184
185
186
187
188 @Override
189 public void doActionTaken(ActionTakenEvent event) {
190 if ((KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(
191 this.getClass().getName()).getUseWorkflowPessimisticLocking()) && (!getNonLockingActionTakenCodes()
192 .contains(event.getActionTaken().getActionTaken().getCode()))) {
193 KRADServiceLocatorWeb.getPessimisticLockService().establishWorkflowPessimisticLocking(this);
194 }
195 }
196
197
198
199
200 @Override
201 public void afterActionTaken(ActionType performed, ActionTakenEvent event) {
202
203 }
204
205
206
207
208
209
210
211 protected List<String> getNonLockingActionTakenCodes() {
212 List<String> actionTakenStatusCodes = new ArrayList<String>();
213 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_SAVED_CD);
214 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD);
215 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_FYI_CD);
216 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_DENIED_CD);
217 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_CANCELED_CD);
218 actionTakenStatusCodes.add(KewApiConstants.ACTION_TAKEN_LOG_DOCUMENT_ACTION_CD);
219 return actionTakenStatusCodes;
220 }
221
222
223
224
225
226
227
228 @Override
229 public void afterWorkflowEngineProcess(boolean successfullyProcessed) {
230 if (KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(
231 this.getClass().getName()).getUseWorkflowPessimisticLocking()) {
232 if (successfullyProcessed) {
233 KRADServiceLocatorWeb.getPessimisticLockService().releaseWorkflowPessimisticLocking(this);
234 }
235 }
236 }
237
238
239
240
241
242
243
244 @Override
245 public void beforeWorkflowEngineProcess() {
246
247 }
248
249
250
251
252
253
254 @Override
255 public List<String> getWorkflowEngineDocumentIdsToLock() {
256 return null;
257 }
258
259
260
261
262 public void toCopy() throws WorkflowException, IllegalStateException {
263 if (!this.getAllowsCopy()) {
264 throw new IllegalStateException(this.getClass().getName() + " does not support document-level copying");
265 }
266 String sourceDocumentHeaderId = getDocumentNumber();
267 setNewDocumentHeader();
268
269
270 this.notes.clear();
271 addCopyErrorDocumentNote("copied from document " + sourceDocumentHeaderId);
272 }
273
274
275
276
277
278
279 protected void setNewDocumentHeader() throws WorkflowException {
280
281 Person user = GlobalVariables.getUserSession().getPerson();
282 WorkflowDocument sourceWorkflowDocument
283 = KRADServiceLocatorWeb.getWorkflowDocumentService().loadWorkflowDocument(getDocumentNumber(), user);
284 String sourceDocumentTypeName = sourceWorkflowDocument.getDocumentTypeName();
285
286
287 WorkflowDocument workflowDocument
288 = KRADServiceLocatorWeb.getWorkflowDocumentService().createWorkflowDocument(sourceDocumentTypeName, user);
289 UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), workflowDocument);
290
291
292 Document newDocument = KRADServiceLocatorWeb.getDocumentService().getNewDocument(sourceDocumentTypeName);
293 DocumentHeader newDocumentHeader = newDocument.getDocumentHeader();
294 newDocumentHeader.setDocumentTemplateNumber(getDocumentNumber());
295 newDocumentHeader.setDocumentDescription(getDocumentHeader().getDocumentDescription());
296 newDocumentHeader.setOrganizationDocumentNumber(getDocumentHeader().getOrganizationDocumentNumber());
297
298
299 try {
300 KRADServiceLocatorWeb.getLegacyDataAdapter().setObjectPropertyDeep(this,
301 KRADPropertyConstants.DOCUMENT_NUMBER, documentNumber.getClass(), newDocument.getDocumentNumber());
302 } catch (Exception e) {
303 LOG.error("Unable to set document number property in copied document " + this, e);
304 throw new RuntimeException("Unable to set document number property in copied document " + this, e);
305 }
306
307
308 setDocumentHeader(newDocument.getDocumentHeader());
309 }
310
311
312
313
314
315
316 protected void addCopyErrorDocumentNote(String noteText) {
317 Note note = null;
318 try {
319 note = KRADServiceLocatorWeb.getDocumentService().createNoteFromDocument(this, noteText);
320 } catch (Exception e) {
321 logErrors();
322 throw new RuntimeException("Couldn't create note on copy or error", e);
323 }
324 addNote(note);
325 }
326
327
328
329
330 @Override
331 public String getXmlForRouteReport() {
332 prepareForSave();
333 populateDocumentForRouting();
334 return getDocumentHeader().getWorkflowDocument().getApplicationContent();
335 }
336
337
338
339
340 @Override
341 public void populateDocumentForRouting() {
342 getDocumentHeader().getWorkflowDocument().setApplicationContent(serializeDocumentToXml());
343 }
344
345
346
347
348 @Override
349 public String serializeDocumentToXml() {
350 DocumentSerializerService documentSerializerService = KRADServiceLocatorWeb.getDocumentSerializerService();
351 String xml = documentSerializerService.serializeDocumentToXmlForRouting(this);
352 return xml;
353 }
354
355
356
357
358
359
360
361 @Override
362 public KualiDocumentXmlMaterializer wrapDocumentWithMetadataForXmlSerialization() {
363 KualiTransactionalDocumentInformation transInfo = new KualiTransactionalDocumentInformation();
364 DocumentInitiator initiator = new DocumentInitiator();
365 String initiatorPrincipalId = getDocumentHeader().getWorkflowDocument().getDocument().getInitiatorPrincipalId();
366 Person initiatorUser = KimApiServiceLocator.getPersonService().getPerson(initiatorPrincipalId);
367 initiator.setPerson(initiatorUser);
368 transInfo.setDocumentInitiator(initiator);
369 KualiDocumentXmlMaterializer xmlWrapper = new KualiDocumentXmlMaterializer();
370 xmlWrapper.setDocument(this);
371 xmlWrapper.setKualiTransactionalDocumentInformation(transInfo);
372 return xmlWrapper;
373 }
374
375
376
377
378
379
380
381
382
383
384 @Override
385 public PropertySerializabilityEvaluator getDocumentPropertySerizabilityEvaluator() {
386 String docTypeName = getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
387 DocumentEntry documentEntry =
388 KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(docTypeName);
389 WorkflowProperties workflowProperties = documentEntry.getWorkflowProperties();
390 WorkflowAttributes workflowAttributes = documentEntry.getWorkflowAttributes();
391 return createPropertySerializabilityEvaluator(workflowProperties, workflowAttributes);
392 }
393
394 protected PropertySerializabilityEvaluator createPropertySerializabilityEvaluator(
395 WorkflowProperties workflowProperties, WorkflowAttributes workflowAttributes) {
396 if (workflowAttributes != null) {
397 return new AlwaysFalsePropertySerializabilityEvaluator();
398 }
399 if (workflowProperties == null) {
400 return new AlwaysTruePropertySerializibilityEvaluator();
401 }
402 PropertySerializabilityEvaluator evaluator = new BusinessObjectPropertySerializibilityEvaluator();
403 evaluator.initializeEvaluatorForDocument(this);
404 return evaluator;
405 }
406
407
408
409
410
411
412
413 @Override
414 public String getBasePathToDocumentDuringSerialization() {
415 return "document";
416 }
417
418
419
420
421 @Override
422 public DocumentHeader getDocumentHeader() {
423
424
425 if ((documentHeader == null || documentHeader.getDocumentNumber() == null) && StringUtils.isNotBlank(documentNumber)) {
426 documentHeader = KRADServiceLocatorWeb.getDocumentHeaderService().getDocumentHeaderById(documentNumber);
427 }
428
429 return this.documentHeader;
430 }
431
432
433
434
435 @Override
436 public void setDocumentHeader(DocumentHeader documentHeader) {
437 this.documentHeader = documentHeader;
438 }
439
440
441
442
443 @Override
444 public String getDocumentNumber() {
445 return documentNumber;
446 }
447
448
449
450
451 @Override
452 public void setDocumentNumber(String documentNumber) {
453 this.documentNumber = documentNumber;
454 }
455
456
457
458
459 @Override
460 public List<AdHocRoutePerson> getAdHocRoutePersons() {
461 return adHocRoutePersons;
462 }
463
464
465
466
467 @Override
468 public void setAdHocRoutePersons(List<AdHocRoutePerson> adHocRoutePersons) {
469 this.adHocRoutePersons = adHocRoutePersons;
470 }
471
472
473
474
475 @Override
476 public List<AdHocRouteWorkgroup> getAdHocRouteWorkgroups() {
477 return adHocRouteWorkgroups;
478 }
479
480
481
482
483 @Override
484 public void setAdHocRouteWorkgroups(List<AdHocRouteWorkgroup> adHocRouteWorkgroups) {
485 this.adHocRouteWorkgroups = adHocRouteWorkgroups;
486 }
487
488 @Override
489 public void postProcessSave(DocumentEvent event) {
490
491
492 }
493
494
495
496
497
498
499 @Override
500 public void prepareForSave(DocumentEvent event) {
501
502 }
503
504 @Override
505 public void validateBusinessRules(DocumentEvent event) {
506 if (GlobalVariables.getMessageMap().hasErrors()) {
507 logErrors();
508 throw new ValidationException("errors occured before business rule");
509 }
510
511
512 LOG.info("invoking rules engine on document " + getDocumentNumber());
513 boolean isValid = true;
514 isValid = KRADServiceLocatorWeb.getKualiRuleService().applyRules(event);
515
516
517 if (!isValid) {
518 logErrors();
519
520
521 throw new ValidationException("business rule evaluation failed");
522 } else if (GlobalVariables.getMessageMap().hasErrors()) {
523 logErrors();
524 throw new ValidationException(
525 "Unreported errors occured during business rule evaluation (rule developer needs to put meaningful error messages into global ErrorMap)");
526 }
527 LOG.debug("validation completed");
528
529 }
530
531
532
533
534 protected void logErrors() {
535 if (LOG.isInfoEnabled()) {
536 if (GlobalVariables.getMessageMap().hasErrors()) {
537
538 for (Iterator<Map.Entry<String, List<ErrorMessage>>> i =
539 GlobalVariables.getMessageMap().getAllPropertiesAndErrors().iterator(); i.hasNext(); ) {
540 Map.Entry<String, List<ErrorMessage>> e = i.next();
541
542 StringBuffer logMessage = new StringBuffer();
543 logMessage.append("[" + e.getKey() + "] ");
544 boolean first = true;
545
546 List<ErrorMessage> errorList = e.getValue();
547 for (Iterator<ErrorMessage> j = errorList.iterator(); j.hasNext(); ) {
548 ErrorMessage em = j.next();
549
550 if (first) {
551 first = false;
552 } else {
553 logMessage.append(";");
554 }
555 logMessage.append(em);
556 }
557
558 LOG.info(logMessage);
559 }
560 }
561 }
562 }
563
564
565
566
567
568
569 @Override
570 public List<DocumentEvent> generateSaveEvents() {
571 return new ArrayList<DocumentEvent>();
572 }
573
574
575
576
577 @Override
578 public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
579
580 }
581
582
583
584
585
586
587
588
589
590
591
592 @Override
593 public GloballyUnique getNoteTarget() {
594 return getDocumentHeader();
595 }
596
597
598
599
600
601
602
603
604
605
606
607
608 @Override
609 public NoteType getNoteType() {
610 return NoteType.DOCUMENT_HEADER;
611 }
612
613
614
615
616 @Override
617 public void addNote(Note note) {
618 if (note == null) {
619 throw new IllegalArgumentException("Note cannot be null.");
620 }
621 notes.add(note);
622 }
623
624
625
626
627 @Override
628 public boolean removeNote(Note note) {
629 if (note == null) {
630 throw new IllegalArgumentException("Note cannot be null.");
631 }
632 return notes.remove(note);
633 }
634
635
636
637
638 @Override
639 public Note getNote(int index) {
640 return notes.get(index);
641 }
642
643
644
645
646 @Override
647 public List<Note> getNotes() {
648 if (CollectionUtils.isEmpty(notes) && getNoteType().equals(NoteType.BUSINESS_OBJECT) && StringUtils.isNotBlank(
649 getNoteTarget().getObjectId())) {
650 notes = getNoteService().getByRemoteObjectId(getNoteTarget().getObjectId());
651 }
652
653 return notes;
654 }
655
656
657
658
659 @Override
660 public void setNotes(List<Note> notes) {
661 if (notes == null) {
662 throw new IllegalArgumentException("List of notes must be non-null.");
663 }
664 this.notes = notes;
665 }
666
667
668
669
670 @Override
671 public List<ActionRequest> getActionRequests() {
672 return KewApiServiceLocator.getWorkflowDocumentService().getPendingActionRequests(getDocumentNumber());
673 }
674
675
676
677
678 @Override
679 public String getSuperUserAnnotation() {
680 return superUserAnnotation;
681 }
682
683
684
685
686 @Override
687 public void setSuperUserAnnotation(String superUserAnnotation) {
688 this.superUserAnnotation = superUserAnnotation;
689 }
690
691
692
693
694
695
696 @PostLoad
697 protected void postLoad() {
698 documentHeader = KRADServiceLocatorWeb.getDocumentHeaderService().getDocumentHeaderById(documentNumber);
699 refreshPessimisticLocks();
700 }
701
702
703
704
705 @PrePersist
706 protected void prePersist() {
707 super.prePersist();
708
709 documentHeader = KRADServiceLocatorWeb.getDocumentHeaderService().saveDocumentHeader(documentHeader);
710 }
711
712
713
714
715
716
717
718 @PostRemove
719 protected void postRemove() {
720 KRADServiceLocatorWeb.getDocumentHeaderService().deleteDocumentHeader(getDocumentHeader());
721 }
722
723
724
725
726 @Override
727 public List<PessimisticLock> getPessimisticLocks() {
728 return pessimisticLocks;
729 }
730
731
732
733
734 @Override
735 public void refreshPessimisticLocks() {
736 pessimisticLocks = KRADServiceLocatorWeb.getPessimisticLockService().getPessimisticLocksForDocument(documentNumber);
737 }
738
739
740
741
742 public void setPessimisticLocks(List<PessimisticLock> pessimisticLocks) {
743 this.pessimisticLocks = pessimisticLocks;
744 }
745
746
747
748
749 @Override
750 public void addPessimisticLock(PessimisticLock lock) {
751 pessimisticLocks.add(lock);
752 }
753
754
755
756
757 @Override
758 @Deprecated
759 public List<String> getLockClearingMethodNames() {
760 return getLockClearningMethodNames();
761 }
762
763
764
765
766 @Override
767 @Deprecated
768 public List<String> getLockClearningMethodNames() {
769 List<String> methodToCalls = new ArrayList<String>();
770 methodToCalls.add(KRADConstants.CLOSE_METHOD);
771 methodToCalls.add(KRADConstants.CANCEL_METHOD);
772
773 methodToCalls.add(KRADConstants.ROUTE_METHOD);
774 methodToCalls.add(KRADConstants.APPROVE_METHOD);
775 methodToCalls.add(KRADConstants.DISAPPROVE_METHOD);
776 methodToCalls.add(KRADConstants.ACKNOWLEDGE_METHOD);
777 return methodToCalls;
778 }
779
780
781
782
783
784
785
786
787 @Override
788 public boolean useCustomLockDescriptors() {
789 return false;
790 }
791
792
793
794
795
796
797
798
799 @Override
800 public String getCustomLockDescriptor(Person user) {
801 throw new PessimisticLockingException("Document " + getDocumentNumber() +
802 " is using pessimistic locking with custom lock descriptors, but the document class has not overriden the getCustomLockDescriptor method");
803 }
804
805 protected AttachmentService getAttachmentService() {
806 if (attachmentService == null) {
807 attachmentService = KRADServiceLocator.getAttachmentService();
808 }
809 return attachmentService;
810 }
811
812 protected NoteService getNoteService() {
813 if (noteService == null) {
814 noteService = KRADServiceLocator.getNoteService();
815 }
816 return noteService;
817 }
818
819
820
821
822
823
824
825
826
827
828 @Deprecated
829 public void refreshReferenceObject(String referenceObjectName) {
830 if ( StringUtils.equals( referenceObjectName, "documentHeader" ) ) {
831 documentHeader = KRADServiceLocatorWeb.getDocumentHeaderService().getDocumentHeaderById(documentNumber);
832 } else {
833 super.refreshReferenceObject(referenceObjectName);
834 }
835 }
836 }