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