1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.kuali.rice.kew.messaging.exceptionhandling;
18
19 import java.lang.reflect.InvocationTargetException;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Iterator;
23 import java.util.List;
24
25 import org.apache.log4j.MDC;
26 import org.kuali.rice.core.exception.RiceRuntimeException;
27 import org.kuali.rice.core.util.ExceptionUtils;
28 import org.kuali.rice.kew.actionitem.ActionItem;
29 import org.kuali.rice.kew.actionrequest.ActionRequestFactory;
30 import org.kuali.rice.kew.actionrequest.ActionRequestValue;
31 import org.kuali.rice.kew.actionrequest.KimGroupRecipient;
32 import org.kuali.rice.kew.engine.RouteContext;
33 import org.kuali.rice.kew.engine.node.RouteNodeInstance;
34 import org.kuali.rice.kew.exception.InvalidActionTakenException;
35 import org.kuali.rice.kew.exception.RouteManagerException;
36 import org.kuali.rice.kew.exception.WorkflowDocumentExceptionRoutingService;
37 import org.kuali.rice.kew.exception.WorkflowRuntimeException;
38 import org.kuali.rice.kew.postprocessor.DocumentRouteStatusChange;
39 import org.kuali.rice.kew.postprocessor.PostProcessor;
40 import org.kuali.rice.kew.postprocessor.ProcessDocReport;
41 import org.kuali.rice.kew.role.RoleRouteModule;
42 import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
43 import org.kuali.rice.kew.service.KEWServiceLocator;
44 import org.kuali.rice.kew.util.KEWConstants;
45 import org.kuali.rice.kew.util.PerformanceLogger;
46 import org.kuali.rice.kns.util.KNSConstants;
47 import org.kuali.rice.ksb.messaging.PersistedMessage;
48 import org.kuali.rice.ksb.service.KSBServiceLocator;
49
50
51 public class ExceptionRoutingServiceImpl implements WorkflowDocumentExceptionRoutingService {
52
53 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ExceptionRoutingServiceImpl.class);
54
55 public void placeInExceptionRouting(String errorMessage, PersistedMessage persistedMessage, Long routeHeaderId) throws Exception {
56 RouteNodeInstance nodeInstance = null;
57 KEWServiceLocator.getRouteHeaderService().lockRouteHeader(routeHeaderId, true);
58 DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(routeHeaderId);
59 RouteContext routeContext = establishRouteContext(document, null);
60 List activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(routeHeaderId);
61 if (!activeNodeInstances.isEmpty()) {
62
63 nodeInstance = (RouteNodeInstance) activeNodeInstances.get(0);
64 }
65 placeInExceptionRouting(errorMessage, nodeInstance, persistedMessage, routeContext, document, true);
66 }
67
68 public void placeInExceptionRouting(Throwable throwable, PersistedMessage persistedMessage, Long routeHeaderId) throws Exception {
69 placeInExceptionRouting(throwable, persistedMessage, routeHeaderId, true);
70 }
71
72
73
74
75
76 public void placeInExceptionRoutingLastDitchEffort(Throwable throwable, PersistedMessage persistedMessage, Long routeHeaderId) throws Exception {
77 placeInExceptionRouting(throwable, persistedMessage, routeHeaderId, false);
78 }
79
80 protected void placeInExceptionRouting(Throwable throwable, PersistedMessage persistedMessage, Long routeHeaderId, boolean invokePostProcessor) throws Exception {
81 KEWServiceLocator.getRouteHeaderService().lockRouteHeader(routeHeaderId, true);
82 DocumentRouteHeaderValue document = KEWServiceLocator.getRouteHeaderService().getRouteHeader(routeHeaderId);
83 throwable = unwrapRouteManagerExceptionIfPossible(throwable);
84 RouteContext routeContext = establishRouteContext(document, throwable);
85 RouteNodeInstance nodeInstance = routeContext.getNodeInstance();
86 Throwable cause = determineActualCause(throwable, 0);
87 String errorMessage = (cause != null && cause.getMessage() != null) ? cause.getMessage() : "";
88 placeInExceptionRouting(errorMessage, nodeInstance, persistedMessage, routeContext, document, invokePostProcessor);
89 }
90
91 protected void placeInExceptionRouting(String errorMessage, RouteNodeInstance nodeInstance, PersistedMessage persistedMessage, RouteContext routeContext, DocumentRouteHeaderValue document, boolean invokePostProcessor) throws Exception {
92 Long routeHeaderId = document.getRouteHeaderId();
93 MDC.put("docId", routeHeaderId);
94 PerformanceLogger performanceLogger = new PerformanceLogger(routeHeaderId);
95 try {
96
97
98 List actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(routeHeaderId);
99 for (Iterator iter = actionRequests.iterator(); iter.hasNext();) {
100 ActionRequestValue actionRequest = (ActionRequestValue) iter.next();
101 if (actionRequest.isActive()) {
102 actionRequest.setStatus(KEWConstants.ACTION_REQUEST_INITIALIZED);
103 for (Iterator iterator = actionRequest.getActionItems().iterator(); iterator.hasNext();) {
104 KEWServiceLocator.getActionListService().deleteActionItem((ActionItem) iterator.next());
105 }
106 KEWServiceLocator.getActionRequestService().saveActionRequest(actionRequest);
107 }
108 }
109
110 LOG.debug("Generating exception request for doc : " + routeHeaderId);
111 if (errorMessage == null) {
112 errorMessage = "";
113 }
114 if (errorMessage.length() > KEWConstants.MAX_ANNOTATION_LENGTH) {
115 errorMessage = errorMessage.substring(0, KEWConstants.MAX_ANNOTATION_LENGTH);
116 }
117 List<ActionRequestValue> exceptionRequests = new ArrayList<ActionRequestValue>();
118 if (nodeInstance.getRouteNode().isExceptionGroupDefined()) {
119 exceptionRequests = generateExceptionGroupRequests(routeContext);
120 } else {
121 exceptionRequests = generateKimExceptionRequests(routeContext);
122 }
123 if (exceptionRequests.isEmpty()) {
124 throw new RiceRuntimeException("Failed to generate exception requests for exception routing!");
125 }
126 activateExceptionRequests(routeContext, exceptionRequests, errorMessage, invokePostProcessor);
127 KSBServiceLocator.getRouteQueueService().delete(persistedMessage);
128 } finally {
129 performanceLogger.log("Time to generate exception request.");
130 MDC.remove("docId");
131 }
132 }
133
134 protected void notifyStatusChange(DocumentRouteHeaderValue routeHeader, String newStatusCode, String oldStatusCode) throws InvalidActionTakenException {
135 DocumentRouteStatusChange statusChangeEvent = new DocumentRouteStatusChange(routeHeader.getRouteHeaderId(), routeHeader.getAppDocId(), oldStatusCode, newStatusCode);
136 try {
137 LOG.debug("Notifying post processor of status change "+oldStatusCode+"->"+newStatusCode);
138 PostProcessor postProcessor = routeHeader.getDocumentType().getPostProcessor();
139 ProcessDocReport report = postProcessor.doRouteStatusChange(statusChangeEvent);
140 if (!report.isSuccess()) {
141 LOG.warn(report.getMessage(), report.getProcessException());
142 throw new InvalidActionTakenException(report.getMessage());
143 }
144 } catch (Exception ex) {
145 LOG.warn(ex, ex);
146 throw new WorkflowRuntimeException(ex);
147 }
148 }
149
150 protected List<ActionRequestValue> generateExceptionGroupRequests(RouteContext routeContext) {
151 RouteNodeInstance nodeInstance = routeContext.getNodeInstance();
152 ActionRequestFactory arFactory = new ActionRequestFactory(routeContext.getDocument(), nodeInstance);
153 ActionRequestValue exceptionRequest = arFactory.createActionRequest(KEWConstants.ACTION_REQUEST_COMPLETE_REQ, new Integer(0), new KimGroupRecipient(nodeInstance.getRouteNode().getExceptionWorkgroup()), "Exception Workgroup for route node " + nodeInstance.getName(), KEWConstants.EXCEPTION_REQUEST_RESPONSIBILITY_ID, Boolean.TRUE, "");
154 return Collections.singletonList(exceptionRequest);
155 }
156
157 protected List<ActionRequestValue> generateKimExceptionRequests(RouteContext routeContext) throws Exception {
158 RoleRouteModule roleRouteModule = new RoleRouteModule();
159 roleRouteModule.setNamespace(KNSConstants.KUALI_RICE_WORKFLOW_NAMESPACE);
160 roleRouteModule.setResponsibilityTemplateName(KEWConstants.EXCEPTION_ROUTING_RESPONSIBILITY_TEMPLATE_NAME);
161 List<ActionRequestValue> requests = roleRouteModule.findActionRequests(routeContext);
162 processExceptionRequests(requests);
163 return requests;
164 }
165
166
167
168
169
170
171
172 protected void processExceptionRequests(List<ActionRequestValue> exceptionRequests) {
173 if (exceptionRequests != null) {
174 for (ActionRequestValue actionRequest : exceptionRequests) {
175 processExceptionRequest(actionRequest);
176 }
177 }
178 }
179
180
181
182
183
184 protected void processExceptionRequest(ActionRequestValue actionRequest) {
185 actionRequest.setForceAction(true);
186 processExceptionRequests(actionRequest.getChildrenRequests());
187 }
188
189
190
191
192
193
194
195
196
197 protected void activateExceptionRequests(RouteContext routeContext, List<ActionRequestValue> exceptionRequests, String exceptionMessage, boolean invokePostProcessor) throws Exception {
198 setExceptionAnnotations(exceptionRequests, exceptionMessage);
199
200 DocumentRouteHeaderValue rh = KEWServiceLocator.getRouteHeaderService().getRouteHeader(routeContext.getDocument().getRouteHeaderId());
201 String oldStatus = rh.getDocRouteStatus();
202 rh.setDocRouteStatus(KEWConstants.ROUTE_HEADER_EXCEPTION_CD);
203 if (invokePostProcessor) {
204 notifyStatusChange(rh, KEWConstants.ROUTE_HEADER_EXCEPTION_CD, oldStatus);
205 }
206 KEWServiceLocator.getRouteHeaderService().saveRouteHeader(rh);
207 KEWServiceLocator.getActionRequestService().activateRequests(exceptionRequests);
208 }
209
210
211
212
213 protected void setExceptionAnnotations(List<ActionRequestValue> actionRequests, String exceptionMessage) {
214 for (ActionRequestValue actionRequest : actionRequests) {
215 actionRequest.setAnnotation(exceptionMessage);
216 }
217 }
218
219 private Throwable unwrapRouteManagerExceptionIfPossible(Throwable throwable) {
220 throwable = ExceptionUtils.unwrapActualCause(throwable);
221 if (throwable != null && (! (throwable instanceof RouteManagerException)) && throwable.getCause() instanceof RouteManagerException) {
222 throwable = throwable.getCause();
223 }
224 return throwable;
225 }
226
227 protected Throwable determineActualCause(Throwable throwable, int depth) {
228 if (depth >= 10) {
229 return throwable;
230 }
231 if ((throwable instanceof InvocationTargetException) || (throwable instanceof RouteManagerException)) {
232 if (throwable.getCause() != null) {
233 return determineActualCause(throwable.getCause(), ++depth);
234 }
235 }
236 return throwable;
237 }
238
239 protected RouteContext establishRouteContext(DocumentRouteHeaderValue document, Throwable throwable) {
240 RouteContext routeContext = new RouteContext();
241 if (throwable instanceof RouteManagerException) {
242 RouteManagerException rmException = (RouteManagerException) throwable;
243 routeContext = rmException.getRouteContext();
244 } else {
245 routeContext.setDocument(document);
246 List activeNodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getRouteHeaderId());
247 if (!activeNodeInstances.isEmpty()) {
248
249 RouteNodeInstance nodeInstance = (RouteNodeInstance) activeNodeInstances.get(0);
250 routeContext.setNodeInstance(nodeInstance);
251 }
252 }
253 if (routeContext.getNodeInstance() == null) {
254
255 routeContext.setNodeInstance((RouteNodeInstance) document.getInitialRouteNodeInstances().get(0));
256 }
257 return routeContext;
258 }
259 }