1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.service.impl;
17
18 import java.util.List;
19 import java.util.concurrent.Callable;
20
21 import org.apache.commons.beanutils.PropertyUtils;
22 import org.apache.log4j.Logger;
23 import org.kuali.rice.kew.api.KewApiConstants;
24 import org.kuali.rice.kew.api.action.ActionType;
25 import org.kuali.rice.kew.api.exception.WorkflowException;
26 import org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent;
27 import org.kuali.rice.kew.framework.postprocessor.AfterProcessEvent;
28 import org.kuali.rice.kew.framework.postprocessor.BeforeProcessEvent;
29 import org.kuali.rice.kew.framework.postprocessor.DeleteEvent;
30 import org.kuali.rice.kew.framework.postprocessor.DocumentLockingEvent;
31 import org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange;
32 import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
33 import org.kuali.rice.kew.framework.postprocessor.ProcessDocReport;
34 import org.kuali.rice.krad.UserSession;
35 import org.kuali.rice.krad.document.Document;
36 import org.kuali.rice.krad.service.DocumentService;
37 import org.kuali.rice.krad.service.PostProcessorService;
38 import org.kuali.rice.krad.util.GlobalVariables;
39 import org.kuali.rice.krad.util.KRADConstants;
40 import org.kuali.rice.krad.util.LegacyUtils;
41 import org.springframework.dao.OptimisticLockingFailureException;
42 import org.springframework.transaction.annotation.Transactional;
43
44
45
46
47
48
49
50
51 @Transactional
52 public class PostProcessorServiceImpl implements PostProcessorService {
53
54 private static final Logger LOG = Logger.getLogger(PostProcessorServiceImpl.class);
55
56 private DocumentService documentService;
57
58 @Override
59 public ProcessDocReport doRouteStatusChange(final DocumentRouteStatusChange statusChangeEvent) throws Exception {
60 return LegacyUtils.doInLegacyContext(statusChangeEvent.getDocumentId(), establishPostProcessorUserSession(), new Callable<ProcessDocReport>() {
61 @Override
62 public ProcessDocReport call() throws Exception {
63
64 try {
65 if (LOG.isInfoEnabled()) {
66 LOG.info(new StringBuilder("started handling route status change from ").append(
67 statusChangeEvent.getOldRouteStatus()).append(" to ").append(
68 statusChangeEvent.getNewRouteStatus()).append(" for document ").append(
69 statusChangeEvent.getDocumentId()));
70 }
71
72 Document document = documentService.getByDocumentHeaderId(statusChangeEvent.getDocumentId());
73 if (document == null) {
74 if (!KewApiConstants.ROUTE_HEADER_CANCEL_CD.equals(statusChangeEvent.getNewRouteStatus())) {
75 throw new RuntimeException("unable to load document " + statusChangeEvent.getDocumentId());
76 }
77 } else {
78 document.doRouteStatusChange(statusChangeEvent);
79
80
81
82
83
84
85
86
87 if (!document.getDocumentHeader().getWorkflowDocument().isSaved()) {
88 document = documentService.updateDocument(document);
89
90 }
91
92 }
93 if (LOG.isInfoEnabled()) {
94 LOG.info(new StringBuilder("finished handling route status change from ").append(
95 statusChangeEvent.getOldRouteStatus()).append(" to ").append(
96 statusChangeEvent.getNewRouteStatus()).append(" for document ").append(
97 statusChangeEvent.getDocumentId()));
98 }
99 } catch (Exception e) {
100 logAndRethrow("route status", e);
101 }
102 return new ProcessDocReport(true, "");
103 }
104 });
105 }
106
107 @Override
108 public ProcessDocReport doRouteLevelChange(final DocumentRouteLevelChange levelChangeEvent) throws Exception {
109 return LegacyUtils.doInLegacyContext(levelChangeEvent.getDocumentId(), establishPostProcessorUserSession(), new Callable<ProcessDocReport>() {
110 @Override
111 public ProcessDocReport call() throws Exception {
112
113
114
115
116 try {
117 if (LOG.isDebugEnabled()) {
118 LOG.debug(new StringBuilder("started handling route level change from ").append(
119 levelChangeEvent.getOldNodeName()).append(" to ").append(
120 levelChangeEvent.getNewNodeName()).append(" for document ").append(
121 levelChangeEvent.getDocumentId()));
122 }
123
124 Document document = documentService.getByDocumentHeaderId(levelChangeEvent.getDocumentId());
125 if (document == null) {
126 throw new RuntimeException("unable to load document " + levelChangeEvent.getDocumentId());
127 }
128 document.populateDocumentForRouting();
129 document.doRouteLevelChange(levelChangeEvent);
130 document.getDocumentHeader().getWorkflowDocument().saveDocumentData();
131 if (LOG.isDebugEnabled()) {
132 LOG.debug(new StringBuilder("finished handling route level change from ").append(
133 levelChangeEvent.getOldNodeName()).append(" to ").append(
134 levelChangeEvent.getNewNodeName()).append(" for document ").append(
135 levelChangeEvent.getDocumentId()));
136 }
137 } catch (Exception e) {
138 logAndRethrow("route level", e);
139 }
140 return new ProcessDocReport(true, "");
141 }
142 });
143 }
144
145 @Override
146 public ProcessDocReport doDeleteRouteHeader(DeleteEvent event) throws Exception {
147 return new ProcessDocReport(true, "");
148 }
149
150 @Override
151 public ProcessDocReport doActionTaken(final ActionTakenEvent event) throws Exception {
152 return LegacyUtils.doInLegacyContext(event.getDocumentId(), establishPostProcessorUserSession(), new Callable<ProcessDocReport>() {
153 @Override
154 public ProcessDocReport call() throws Exception {
155 try {
156 if (LOG.isDebugEnabled()) {
157 LOG.debug(new StringBuilder("started doing action taken for action taken code").append(
158 event.getActionTaken().getActionTaken()).append(" for document ").append(
159 event.getDocumentId()));
160 }
161 Document document = documentService.getByDocumentHeaderId(event.getDocumentId());
162 if (document == null) {
163
164 if (!KewApiConstants.ACTION_TAKEN_CANCELED.equals(event.getActionTaken())) {
165 LOG.warn("doActionTaken() Unable to load document with id " + event.getDocumentId() +
166 " using action taken code '" + KewApiConstants.ACTION_TAKEN_CD.get(
167 event.getActionTaken().getActionTaken()));
168 }
169 } else {
170 document.doActionTaken(event);
171 if (LOG.isDebugEnabled()) {
172 LOG.debug(new StringBuilder("finished doing action taken for action taken code").append(
173 event.getActionTaken().getActionTaken()).append(" for document ").append(
174 event.getDocumentId()));
175 }
176 }
177 } catch (Exception e) {
178 logAndRethrow("do action taken", e);
179 }
180 return new ProcessDocReport(true, "");
181
182 }
183 });
184 }
185
186 @Override
187 public ProcessDocReport afterActionTaken(final ActionType performed,
188 final ActionTakenEvent event) throws Exception {
189 return LegacyUtils.doInLegacyContext(event.getDocumentId(), establishPostProcessorUserSession(), new Callable<ProcessDocReport>() {
190 @Override
191 public ProcessDocReport call() throws Exception {
192 try {
193 if (LOG.isDebugEnabled()) {
194 LOG.debug(new StringBuilder("started doing after action taken for action performed code "
195 + performed.getCode()
196 + " and action taken code ").append(event.getActionTaken().getActionTaken()).append(
197 " for document ").append(event.getDocumentId()));
198 }
199 Document document = documentService.getByDocumentHeaderId(event.getDocumentId());
200 if (document == null) {
201
202 if (!KewApiConstants.ACTION_TAKEN_CANCELED.equals(event.getActionTaken())) {
203 LOG.warn("afterActionTaken() Unable to load document with id " + event.getDocumentId() +
204 " using action taken code '" + KewApiConstants.ACTION_TAKEN_CD.get(
205 event.getActionTaken().getActionTaken()));
206 }
207 } else {
208 document.afterActionTaken(performed, event);
209 if (LOG.isDebugEnabled()) {
210 LOG.debug(new StringBuilder("finished doing after action taken for action taken code")
211 .append(event.getActionTaken().getActionTaken()).append(" for document ").append(
212 event.getDocumentId()));
213 }
214 }
215 } catch (Exception e) {
216 logAndRethrow("do action taken", e);
217 }
218 return new ProcessDocReport(true, "");
219
220 }
221 });
222 }
223
224
225
226
227
228
229 @Override
230 public ProcessDocReport afterProcess(final AfterProcessEvent event) throws Exception {
231 return LegacyUtils.doInLegacyContext(event.getDocumentId(), establishPostProcessorUserSession(), new Callable<ProcessDocReport>() {
232 @Override
233 public ProcessDocReport call() throws Exception {
234
235 try {
236 if (LOG.isDebugEnabled()) {
237 LOG.debug(new StringBuilder("started after process method for document ").append(
238 event.getDocumentId()));
239 }
240
241 Document document = documentService.getByDocumentHeaderId(event.getDocumentId());
242 if (document == null) {
243
244 LOG.warn("afterProcess() Unable to load document with id "
245 + event.getDocumentId()
246 + "... ignoring post processing");
247 } else {
248 document.afterWorkflowEngineProcess(event.isSuccessfullyProcessed());
249 if (LOG.isDebugEnabled()) {
250 LOG.debug(new StringBuilder("finished after process method for document ").append(
251 event.getDocumentId()));
252 }
253 }
254 } catch (Exception e) {
255 logAndRethrow("after process", e);
256 }
257 return new ProcessDocReport(true, "");
258 }
259 });
260 }
261
262
263
264
265
266 @Override
267 public ProcessDocReport beforeProcess(final BeforeProcessEvent event) throws Exception {
268 return LegacyUtils.doInLegacyContext(event.getDocumentId(), establishPostProcessorUserSession(), new Callable<ProcessDocReport>() {
269 @Override
270 public ProcessDocReport call() throws Exception {
271
272 try {
273 if (LOG.isDebugEnabled()) {
274 LOG.debug(new StringBuilder("started before process method for document ").append(
275 event.getDocumentId()));
276 }
277 Document document = documentService.getByDocumentHeaderId(event.getDocumentId());
278 if (document == null) {
279
280 LOG.warn("beforeProcess() Unable to load document with id "
281 + event.getDocumentId()
282 + "... ignoring post processing");
283 } else {
284 document.beforeWorkflowEngineProcess();
285 if (LOG.isDebugEnabled()) {
286 LOG.debug(new StringBuilder("finished before process method for document ").append(
287 event.getDocumentId()));
288 }
289 }
290 } catch (Exception e) {
291 logAndRethrow("before process", e);
292 }
293 return new ProcessDocReport(true, "");
294 }
295 });
296 }
297
298
299
300
301
302
303 @Override
304 public List<String> getDocumentIdsToLock(final DocumentLockingEvent event) throws Exception {
305 return LegacyUtils.doInLegacyContext(event.getDocumentId(), establishPostProcessorUserSession(), new Callable<List<String>>() {
306 @Override
307 public List<String> call() throws Exception {
308
309 try {
310 if (LOG.isDebugEnabled()) {
311 LOG.debug(new StringBuilder("started get document ids to lock method for document ").append(
312 event.getDocumentId()));
313 }
314 Document document = documentService.getByDocumentHeaderId(event.getDocumentId());
315 if (document == null) {
316
317 LOG.warn("getDocumentIdsToLock() Unable to load document with id "
318 + event.getDocumentId()
319 + "... ignoring post processing");
320 } else {
321 List<String> documentIdsToLock = document.getWorkflowEngineDocumentIdsToLock();
322 if (LOG.isDebugEnabled()) {
323 LOG.debug(new StringBuilder("finished get document ids to lock method for document ").append(
324 event.getDocumentId()));
325 }
326 if (documentIdsToLock == null) {
327 return null;
328 }
329 return documentIdsToLock;
330 }
331 } catch (Exception e) {
332 logAndRethrow("before process", e);
333 }
334 return null;
335 }
336 });
337 }
338
339 private void logAndRethrow(String changeType, Exception e) throws RuntimeException {
340 LOG.error("caught exception while handling " + changeType + " change", e);
341 logOptimisticDetails(5, e);
342
343 throw new RuntimeException("post processor caught exception while handling " + changeType + " change: " + e.getMessage(), e);
344 }
345
346
347
348
349
350
351
352 private void logOptimisticDetails(int depth, Throwable t) {
353 if ((depth > 0) && (t != null)) {
354 Object sourceObject = null;
355 boolean optLockException = false;
356 if ( t instanceof javax.persistence.OptimisticLockException ) {
357 sourceObject = ((javax.persistence.OptimisticLockException)t).getEntity();
358 optLockException = true;
359 } else if ( t instanceof OptimisticLockingFailureException ) {
360 sourceObject = ((OptimisticLockingFailureException)t).getMessage();
361 optLockException = true;
362 } else if ( t.getClass().getName().equals( "org.apache.ojb.broker.OptimisticLockException" ) ) {
363 try {
364 sourceObject = PropertyUtils.getSimpleProperty(t, "sourceObject");
365 } catch (Exception ex) {
366 LOG.warn( "Unable to retrieve source object from OJB OptimisticLockException", ex );
367 }
368 optLockException = true;
369 }
370 if ( optLockException ) {
371 if (sourceObject != null) {
372 if ( sourceObject instanceof String ) {
373 LOG.error("source of OptimisticLockException Unknown. Message: " + sourceObject);
374 } else {
375 LOG.error("source of OptimisticLockException = " + sourceObject.getClass().getName() + " ::= " + sourceObject);
376 }
377 }
378 } else {
379 Throwable cause = t.getCause();
380 if (cause != t) {
381 logOptimisticDetails(--depth, cause);
382 }
383 }
384 }
385 }
386
387
388
389
390
391 public final void setDocumentService(DocumentService documentService) {
392 this.documentService = documentService;
393 }
394
395
396
397
398 protected UserSession establishPostProcessorUserSession() throws WorkflowException {
399 if (GlobalVariables.getUserSession() == null) {
400 return new UserSession(KRADConstants.SYSTEM_USER);
401 } else {
402 return GlobalVariables.getUserSession();
403 }
404 }
405
406 }