1
2
3
4
5
6
7
8
9
10
11 package org.mule.exception;
12
13 import org.mule.DefaultMuleEvent;
14 import org.mule.DefaultMuleMessage;
15 import org.mule.RequestContext;
16 import org.mule.api.MessagingException;
17 import org.mule.api.MuleContext;
18 import org.mule.api.MuleEvent;
19 import org.mule.api.MuleEventContext;
20 import org.mule.api.MuleException;
21 import org.mule.api.MuleMessage;
22 import org.mule.api.config.MuleProperties;
23 import org.mule.api.construct.FlowConstruct;
24 import org.mule.api.context.MuleContextAware;
25 import org.mule.api.endpoint.EndpointURI;
26 import org.mule.api.endpoint.ImmutableEndpoint;
27 import org.mule.api.endpoint.OutboundEndpoint;
28 import org.mule.api.lifecycle.Disposable;
29 import org.mule.api.lifecycle.Initialisable;
30 import org.mule.api.lifecycle.InitialisationException;
31 import org.mule.api.processor.MessageProcessor;
32 import org.mule.api.routing.OutboundRouter;
33 import org.mule.api.service.Service;
34 import org.mule.api.transaction.Transaction;
35 import org.mule.api.transaction.TransactionException;
36 import org.mule.api.transport.DispatchException;
37 import org.mule.api.util.StreamCloserService;
38 import org.mule.config.ExceptionHelper;
39 import org.mule.config.i18n.CoreMessages;
40 import org.mule.context.notification.ExceptionNotification;
41 import org.mule.message.ExceptionMessage;
42 import org.mule.routing.filters.WildcardFilter;
43 import org.mule.routing.outbound.MulticastingRouter;
44 import org.mule.session.DefaultMuleSession;
45 import org.mule.transaction.TransactionCoordination;
46 import org.mule.util.CollectionUtils;
47
48 import java.io.Serializable;
49 import java.util.ArrayList;
50 import java.util.List;
51 import java.util.concurrent.CopyOnWriteArrayList;
52 import java.util.concurrent.atomic.AtomicBoolean;
53
54 import org.apache.commons.logging.Log;
55 import org.apache.commons.logging.LogFactory;
56
57
58
59
60
61
62
63
64 public abstract class AbstractExceptionListener implements Initialisable, Disposable, MuleContextAware
65 {
66
67
68
69 protected transient Log logger = LogFactory.getLog(getClass());
70
71 @SuppressWarnings("unchecked")
72 protected List<MessageProcessor> messageProcessors = new CopyOnWriteArrayList();
73
74 protected AtomicBoolean initialised = new AtomicBoolean(false);
75
76 protected MuleContext muleContext;
77
78 protected WildcardFilter rollbackTxFilter;
79 protected WildcardFilter commitTxFilter;
80
81 protected boolean enableNotifications = true;
82
83 public void setMuleContext(MuleContext context)
84 {
85 this.muleContext = context;
86 }
87
88 public List<MessageProcessor> getMessageProcessors()
89 {
90 return messageProcessors;
91 }
92
93 public void setMessageProcessors(List<OutboundEndpoint> processors)
94 {
95 if (processors != null)
96 {
97 this.messageProcessors.clear();
98 this.messageProcessors.addAll(processors);
99 }
100 else
101 {
102 throw new IllegalArgumentException("List of targets = null");
103 }
104 }
105
106 public void addEndpoint(MessageProcessor processor)
107 {
108 if (processor != null)
109 {
110 messageProcessors.add(processor);
111 }
112 }
113
114 public boolean removeMessageProcessor(MessageProcessor processor)
115 {
116 return messageProcessors.remove(processor);
117 }
118
119 protected Throwable getExceptionType(Throwable t, Class exceptionType)
120 {
121 while (t != null)
122 {
123 if (exceptionType.isAssignableFrom(t.getClass()))
124 {
125 return t;
126 }
127
128 t = t.getCause();
129 }
130
131 return null;
132 }
133
134
135
136
137
138
139
140
141
142 public final synchronized void initialise() throws InitialisationException
143 {
144 if (!initialised.get())
145 {
146 doInitialise(muleContext);
147 initialised.set(true);
148 }
149 }
150
151 protected void doInitialise(MuleContext muleContext) throws InitialisationException
152 {
153 logger.info("Initialising exception listener: " + toString());
154 }
155
156
157
158
159
160
161 protected void handleTransaction(Throwable t)
162 {
163 Transaction tx = TransactionCoordination.getInstance().getTransaction();
164
165 if (tx == null)
166 {
167 return;
168 }
169
170 t = ExceptionHelper.getRootException(t);
171
172 if (rollbackTxFilter == null && commitTxFilter == null)
173 {
174
175 rollbackTransaction();
176 }
177 else if (rollbackTxFilter != null && rollbackTxFilter.accept(t.getClass().getName()))
178 {
179
180 rollbackTransaction();
181 }
182 else if (commitTxFilter != null && !commitTxFilter.accept(t.getClass().getName()))
183 {
184
185 rollbackTransaction();
186 }
187 }
188
189 protected void rollbackTransaction()
190 {
191 Transaction tx = TransactionCoordination.getInstance().getTransaction();
192 try
193 {
194 if (tx != null)
195 {
196 tx.setRollbackOnly();
197 }
198 }
199 catch (TransactionException e)
200 {
201 logException(e);
202 }
203 }
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220 protected void routeException(MuleMessage message, MessageProcessor target, Throwable t)
221 {
222 List endpoints = getMessageProcessors(t);
223 if (CollectionUtils.isNotEmpty(endpoints))
224 {
225 try
226 {
227 logger.error("Message being processed is: " + (message == null ? "null" : message.toString()));
228 MuleEventContext ctx = RequestContext.getEventContext();
229 String component = "Unknown";
230 EndpointURI endpointUri = null;
231 if (ctx != null)
232 {
233 if (ctx.getFlowConstruct() != null)
234 {
235 component = ctx.getFlowConstruct().getName();
236 }
237 endpointUri = ctx.getEndpointURI();
238 }
239 else if (target instanceof ImmutableEndpoint)
240 {
241 endpointUri = ((ImmutableEndpoint)target).getEndpointURI();
242 }
243
244
245
246 Serializable payload;
247 if (message.getPayload() instanceof Serializable)
248 {
249 payload = (Serializable) message.getPayload();
250 }
251 else
252 {
253 payload = message.getPayloadAsString();
254 }
255 ExceptionMessage msg = new ExceptionMessage(payload, t, component, endpointUri);
256
257 MuleMessage exceptionMessage;
258 if (ctx == null)
259 {
260 exceptionMessage = new DefaultMuleMessage(msg, muleContext);
261 }
262 else
263 {
264 exceptionMessage = new DefaultMuleMessage(msg, ctx.getMessage(), muleContext);
265 }
266
267 if (ctx != null && ctx.getFlowConstruct() != null && ctx.getFlowConstruct() instanceof Service)
268 {
269 OutboundRouter router = createOutboundRouter();
270 router.process(new DefaultMuleEvent(exceptionMessage, RequestContext.getEvent()));
271 }
272 else
273 {
274
275
276 customRouteExceptionMessage(exceptionMessage);
277 }
278 }
279 catch (Exception e)
280 {
281 logFatal(message, e);
282 closeStream(message);
283 }
284 }
285 else
286 {
287 handleTransaction(t);
288 closeStream(message);
289 }
290 }
291
292 private void customRouteExceptionMessage(MuleMessage exceptionMessage)
293 throws MessagingException, MuleException, DispatchException
294 {
295
296
297
298 for (int i = 0; i < messageProcessors.size(); i++)
299 {
300 MessageProcessor processor = messageProcessors.get(i);
301 if (((DefaultMuleMessage) exceptionMessage).isConsumable())
302 {
303 throw new MessagingException(
304 CoreMessages.cannotCopyStreamPayload(exceptionMessage.getPayload().getClass().getName()),
305 exceptionMessage);
306 }
307
308 MuleMessage clonedMessage = new DefaultMuleMessage(exceptionMessage.getPayload(),
309 exceptionMessage, muleContext);
310 MuleEvent exceptionEvent = null;
311 if (processor instanceof OutboundEndpoint)
312 {
313 exceptionEvent = new DefaultMuleEvent(clonedMessage, (OutboundEndpoint) processor,
314 new DefaultMuleSession(muleContext));
315 }
316 else
317 {
318 exceptionEvent = new DefaultMuleEvent(clonedMessage, RequestContext.getEvent().getEndpoint(),
319 new DefaultMuleSession(muleContext));
320 }
321 exceptionEvent = RequestContext.setEvent(exceptionEvent);
322
323 processor.process(exceptionEvent);
324
325 if (logger.isDebugEnabled())
326 {
327 logger.debug("routed Exception message via " + processor);
328 }
329 }
330 }
331
332 protected OutboundRouter createOutboundRouter() throws MuleException
333 {
334
335
336
337
338
339 MulticastingRouter router = new MulticastingRouter()
340 {
341 @Override
342 protected void setMessageProperties(FlowConstruct session,
343 MuleMessage message,
344 MessageProcessor target)
345 {
346
347
348 }
349 };
350 router.setRoutes(new ArrayList<MessageProcessor>(getMessageProcessors()));
351 router.setMuleContext(muleContext);
352 return router;
353 }
354
355 protected void closeStream(MuleMessage message)
356 {
357 if (muleContext == null || muleContext.isDisposing() || muleContext.isDisposed())
358 {
359 return;
360 }
361 if (message != null
362 && muleContext.getRegistry().lookupObject(MuleProperties.OBJECT_MULE_STREAM_CLOSER_SERVICE) != null)
363 {
364 ((StreamCloserService) muleContext.getRegistry().lookupObject(
365 MuleProperties.OBJECT_MULE_STREAM_CLOSER_SERVICE)).closeStream(message.getPayload());
366 }
367 }
368
369
370
371
372
373
374
375
376
377
378
379 protected List<MessageProcessor> getMessageProcessors(Throwable t)
380 {
381 if (!messageProcessors.isEmpty())
382 {
383 return messageProcessors;
384 }
385 else
386 {
387 return null;
388 }
389 }
390
391
392
393
394
395
396 protected void logException(Throwable t)
397 {
398 MuleException muleException = ExceptionHelper.getRootMuleException(t);
399 if (muleException != null)
400 {
401 logger.error(muleException.getDetailedMessage());
402 }
403 else
404 {
405 logger.error("Caught exception in Exception Strategy: " + t.getMessage(), t);
406 }
407 }
408
409
410
411
412
413
414
415
416
417 protected void logFatal(MuleMessage message, Throwable t)
418 {
419 logger.fatal(
420 "Failed to dispatch message to error queue after it failed to process. This may cause message loss."
421 + (message == null ? "" : "Logging Message here: \n" + message.toString()), t);
422 }
423
424 public boolean isInitialised()
425 {
426 return initialised.get();
427 }
428
429 public void dispose()
430 {
431
432 }
433
434
435
436
437
438
439
440
441 protected void fireNotification(ExceptionNotification notification)
442 {
443 if (muleContext != null)
444 {
445 muleContext.fireNotification(notification);
446 }
447 else if (logger.isWarnEnabled())
448 {
449 logger.debug("MuleContext is not yet available for firing notifications, ignoring event: "
450 + notification);
451 }
452 }
453
454 public WildcardFilter getCommitTxFilter()
455 {
456 return commitTxFilter;
457 }
458
459 public void setCommitTxFilter(WildcardFilter commitTxFilter)
460 {
461 this.commitTxFilter = commitTxFilter;
462 }
463
464 public boolean isEnableNotifications()
465 {
466 return enableNotifications;
467 }
468
469 public void setEnableNotifications(boolean enableNotifications)
470 {
471 this.enableNotifications = enableNotifications;
472 }
473
474 public WildcardFilter getRollbackTxFilter()
475 {
476 return rollbackTxFilter;
477 }
478
479 public void setRollbackTxFilter(WildcardFilter rollbackTxFilter)
480 {
481 this.rollbackTxFilter = rollbackTxFilter;
482 }
483 }