Coverage Report - org.mule.impl.AbstractExceptionListener
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractExceptionListener
36%
37/102
38%
18/48
2.4
 
 1  
 /*
 2  
  * $Id: AbstractExceptionListener.java 8597 2007-09-26 09:23:59Z dirk.olmes $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
 5  
  *
 6  
  * The software in this package is published under the terms of the CPAL v1.0
 7  
  * license, a copy of which has been included with this distribution in the
 8  
  * LICENSE.txt file.
 9  
  */
 10  
 
 11  
 package org.mule.impl;
 12  
 
 13  
 import org.mule.MuleManager;
 14  
 import org.mule.config.ExceptionHelper;
 15  
 import org.mule.impl.internal.notifications.ExceptionNotification;
 16  
 import org.mule.impl.message.ExceptionMessage;
 17  
 import org.mule.providers.NullPayload;
 18  
 import org.mule.transaction.TransactionCoordination;
 19  
 import org.mule.umo.MessagingException;
 20  
 import org.mule.umo.TransactionException;
 21  
 import org.mule.umo.UMOEvent;
 22  
 import org.mule.umo.UMOEventContext;
 23  
 import org.mule.umo.UMOException;
 24  
 import org.mule.umo.UMOMessage;
 25  
 import org.mule.umo.UMOTransaction;
 26  
 import org.mule.umo.endpoint.UMOEndpoint;
 27  
 import org.mule.umo.endpoint.UMOEndpointURI;
 28  
 import org.mule.umo.endpoint.UMOImmutableEndpoint;
 29  
 import org.mule.umo.lifecycle.Initialisable;
 30  
 import org.mule.umo.lifecycle.InitialisationException;
 31  
 import org.mule.umo.lifecycle.LifecycleException;
 32  
 import org.mule.umo.routing.RoutingException;
 33  
 
 34  
 import java.beans.ExceptionListener;
 35  
 import java.util.Iterator;
 36  
 import java.util.List;
 37  
 
 38  
 import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
 39  
 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
 40  
 import org.apache.commons.logging.Log;
 41  
 import org.apache.commons.logging.LogFactory;
 42  
 
 43  
 /**
 44  
  * <code>AbstractExceptionListener</code> is a base implementation that custom
 45  
  * Exception Listeners can override. It provides template methods for handling the
 46  
  * for base types of exceptions plus allows multimple endpoints to be associated with
 47  
  * this exception listener and provides an implementation for dispatching exception
 48  
  * events from this Listener.
 49  
  */
 50  2796
 public abstract class AbstractExceptionListener implements ExceptionListener, Initialisable
 51  
 {
 52  
     /**
 53  
      * logger used by this class
 54  
      */
 55  2796
     protected transient Log logger = LogFactory.getLog(getClass());
 56  
 
 57  2796
     protected List endpoints = new CopyOnWriteArrayList();
 58  
 
 59  2796
     protected AtomicBoolean initialised = new AtomicBoolean(false);
 60  
 
 61  
     public List getEndpoints()
 62  
     {
 63  0
         return endpoints;
 64  
     }
 65  
 
 66  
     public void setEndpoints(List endpoints)
 67  
     {
 68  0
         for (Iterator iterator = endpoints.iterator(); iterator.hasNext();)
 69  
         {
 70  0
             addEndpoint((UMOEndpoint) iterator.next());
 71  
         }
 72  0
     }
 73  
 
 74  
     public void addEndpoint(UMOEndpoint endpoint)
 75  
     {
 76  0
         if (endpoint != null)
 77  
         {
 78  0
             endpoint.setType(UMOEndpoint.ENDPOINT_TYPE_SENDER);
 79  0
             endpoints.add(endpoint);
 80  
         }
 81  0
     }
 82  
 
 83  
     public boolean removeEndpoint(UMOEndpoint endpoint)
 84  
     {
 85  0
         return endpoints.remove(endpoint);
 86  
     }
 87  
 
 88  
     public void exceptionThrown(Exception e)
 89  
     {
 90  4
         fireNotification(new ExceptionNotification(e));
 91  4
         logException(e);
 92  
 
 93  10
         Throwable t = getExceptionType(e, RoutingException.class);
 94  4
         if (t != null)
 95  
         {
 96  0
             RoutingException re = (RoutingException) t;
 97  0
             handleRoutingException(re.getUmoMessage(), re.getEndpoint(), e);
 98  0
             return;
 99  
         }
 100  
 
 101  4
         t = getExceptionType(e, MessagingException.class);
 102  4
         if (t != null)
 103  
         {
 104  0
             MessagingException me = (MessagingException) t;
 105  0
             handleMessagingException(me.getUmoMessage(), e);
 106  0
             return;
 107  
         }
 108  
 
 109  4
         t = getExceptionType(e, LifecycleException.class);
 110  4
         if (t != null)
 111  
         {
 112  0
             LifecycleException le = (LifecycleException) t;
 113  0
             handleLifecycleException(le.getComponent(), e);
 114  0
             if (RequestContext.getEventContext() != null)
 115  
             {
 116  0
                 handleMessagingException(RequestContext.getEventContext().getMessage(), e);
 117  
             }
 118  
             else
 119  
             {
 120  0
                 logger.info("There is no current event available, routing Null message with the exception");
 121  0
                 handleMessagingException(new MuleMessage(NullPayload.getInstance()), e);
 122  
             }
 123  0
             return;
 124  
         }
 125  
 
 126  4
         handleStandardException(e);
 127  4
     }
 128  
 
 129  
     protected Throwable getExceptionType(Throwable t, Class exceptionType)
 130  
     {
 131  24
         while (t != null)
 132  
         {
 133  12
             if (exceptionType.isAssignableFrom(t.getClass()))
 134  
             {
 135  0
                 return t;
 136  
             }
 137  
 
 138  12
             t = t.getCause();
 139  
         }
 140  
 
 141  12
         return null;
 142  
     }
 143  
 
 144  
     /**
 145  
      * The initialise method is call every time the Exception stategy is assigned to
 146  
      * a component or connector. This implementation ensures that initialise is
 147  
      * called only once. The actual initialisation code is contained in the
 148  
      * <code>doInitialise()</code> method.
 149  
      * 
 150  
      * @throws InitialisationException
 151  
      */
 152  
     public final synchronized void initialise() throws InitialisationException
 153  
     {
 154  574
         if (!initialised.get())
 155  
         {
 156  508
             doInitialise();
 157  508
             initialised.set(true);
 158  
         }
 159  574
     }
 160  
 
 161  
     protected void doInitialise() throws InitialisationException
 162  
     {
 163  508
         logger.info("Initialising exception listener: " + toString());
 164  508
         for (Iterator iterator = endpoints.iterator(); iterator.hasNext();)
 165  
         {
 166  0
             UMOEndpoint umoEndpoint = (UMOEndpoint) iterator.next();
 167  0
             umoEndpoint.initialise();
 168  0
         }
 169  508
     }
 170  
 
 171  
     /**
 172  
      * If there is a current transaction this method will mark it for rollback This
 173  
      * method should not be called if an event is routed from this exception handler
 174  
      * to an endpoint that should take part in the current transaction
 175  
      */
 176  
     protected void markTransactionForRollback()
 177  
     {
 178  8
         UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
 179  
         try
 180  
         {
 181  8
             if (tx != null)
 182  
             {
 183  0
                 tx.setRollbackOnly();
 184  
             }
 185  
         }
 186  0
         catch (TransactionException e)
 187  
         {
 188  0
             logException(e);
 189  8
         }
 190  8
     }
 191  
 
 192  
     /**
 193  
      * Routes the current exception to an error endpoint such as a Dead Letter Queue
 194  
      * (jms) This method is only invoked if there is a UMOMessage available to
 195  
      * dispatch. The message dispatched from this method will be an
 196  
      * <code>ExceptionMessage</code> which contains the exception thrown the
 197  
      * UMOMessage and any context information.
 198  
      * 
 199  
      * @param message the UMOMessage being processed when the exception occurred
 200  
      * @param failedEndpoint optional; the endpoint being dispatched or received on
 201  
      *            when the error occurred. This is NOT the endpoint that the message
 202  
      *            will be disptched on and is only supplied to this method for
 203  
      *            logging purposes
 204  
      * @param t the exception thrown. This will be sent with the ExceptionMessage
 205  
      * @see ExceptionMessage
 206  
      */
 207  
     protected void routeException(UMOMessage message, UMOImmutableEndpoint failedEndpoint, Throwable t)
 208  
     {
 209  4
         UMOEndpoint endpoint = getEndpoint(t);
 210  4
         if (endpoint != null)
 211  
         {
 212  
             try
 213  
             {
 214  0
                 logger.error("Message being processed is: " + (message == null ? "null" : message.toString()));
 215  0
                 UMOEventContext ctx = RequestContext.getEventContext();
 216  0
                 String component = "Unknown";
 217  0
                 UMOEndpointURI endpointUri = null;
 218  0
                 if (ctx != null)
 219  
                 {
 220  0
                     if (ctx.getComponentDescriptor() != null)
 221  
                     {
 222  0
                         component = ctx.getComponentDescriptor().getName();
 223  
                     }
 224  0
                     endpointUri = ctx.getEndpointURI();
 225  
                 }
 226  0
                 else if (failedEndpoint != null)
 227  
                 {
 228  0
                     endpointUri = failedEndpoint.getEndpointURI();
 229  
                 }
 230  
                 ExceptionMessage msg;
 231  0
                 msg = new ExceptionMessage(getErrorMessagePayload(message), t, component, endpointUri);
 232  
 
 233  
                 UMOMessage exceptionMessage;
 234  0
                 if (ctx == null)
 235  
                 {
 236  0
                     exceptionMessage = new MuleMessage(msg);
 237  
                 }
 238  
                 else
 239  
                 {
 240  0
                     exceptionMessage = new MuleMessage(msg, ctx.getMessage());
 241  
                 }
 242  0
                 UMOEvent exceptionEvent = new MuleEvent(exceptionMessage, endpoint, new MuleSession(
 243  
                     exceptionMessage, new MuleSessionHandler()), true);
 244  0
                 exceptionEvent = RequestContext.setEvent(exceptionEvent);
 245  0
                 endpoint.send(exceptionEvent);
 246  
 
 247  0
                 if (logger.isDebugEnabled())
 248  
                 {
 249  0
                     logger.debug("routed Exception message via " + endpoint);
 250  
                 }
 251  
 
 252  
             }
 253  0
             catch (UMOException e)
 254  
             {
 255  0
                 logFatal(message, e);
 256  0
             }
 257  
         }
 258  
         else
 259  
         {
 260  4
             markTransactionForRollback();
 261  
         }
 262  4
     }
 263  
 
 264  
     protected Object getErrorMessagePayload(UMOMessage message)
 265  
     {
 266  
         try
 267  
         {
 268  0
             return message.getPayloadAsString();
 269  
         }
 270  0
         catch (Exception e)
 271  
         {
 272  0
             logException(e);
 273  0
             logger.info("Failed to read message payload as string, using raw payload");
 274  0
             return message.getPayload();
 275  
         }
 276  
     }
 277  
 
 278  
     /**
 279  
      * Returns an endpoint for the given exception. ExceptionListeners can have
 280  
      * multiple endpoints registered on them. This methods allows custom
 281  
      * implementations to control which endpoint is used based on the exception
 282  
      * thrown. This implementation simply returns the first endpoint in the list.
 283  
      * 
 284  
      * @param t the exception thrown
 285  
      * @return The endpoint used to dispatch an exception message on or null if there
 286  
      *         are no endpoints registered
 287  
      */
 288  
     protected UMOEndpoint getEndpoint(Throwable t)
 289  
     {
 290  4
         if (endpoints.size() > 0)
 291  
         {
 292  0
             return (UMOEndpoint) endpoints.get(0);
 293  
         }
 294  
         else
 295  
         {
 296  4
             return null;
 297  
         }
 298  
     }
 299  
 
 300  
     /**
 301  
      * Used to log the error passed into this Exception Listener
 302  
      * 
 303  
      * @param t the exception thrown
 304  
      */
 305  
     protected void logException(Throwable t)
 306  
     {
 307  0
         UMOException umoe = ExceptionHelper.getRootMuleException(t);
 308  0
         if (umoe != null)
 309  
         {
 310  0
             logger.error(umoe.getDetailedMessage());
 311  
         }
 312  
         else
 313  
         {
 314  0
             logger.error("Caught exception in Exception Strategy: " + t.getMessage(), t);
 315  
         }
 316  0
     }
 317  
 
 318  
     /**
 319  
      * Logs a fatal error message to the logging system. This should be used mostly
 320  
      * if an error occurs in the exception listener itself. This implementation logs
 321  
      * the the message itself to the logs if it is not null
 322  
      * 
 323  
      * @param message The UMOMessage currently being processed
 324  
      * @param t the fatal exception to log
 325  
      */
 326  
     protected void logFatal(UMOMessage message, Throwable t)
 327  
     {
 328  0
         logger.fatal(
 329  
             "Failed to dispatch message to error queue after it failed to process.  This may cause message loss."
 330  
                             + (message == null ? "" : "Logging Message here: \n" + message.toString()), t);
 331  0
     }
 332  
 
 333  
     public boolean isInitialised()
 334  
     {
 335  0
         return initialised.get();
 336  
     }
 337  
 
 338  
     /**
 339  
      * Fires a server notification to all registered
 340  
      * {@link org.mule.impl.internal.notifications.ExceptionNotificationListener}
 341  
      * eventManager.
 342  
      *
 343  
      * @param notification the notification to fire.
 344  
      */
 345  
     protected void fireNotification(ExceptionNotification notification)
 346  
     {
 347  4
         MuleManager.getInstance().fireNotification(notification);
 348  4
     }
 349  
 
 350  
     /**
 351  
      * A messaging exception is thrown when an excpetion occurs during normal message
 352  
      * processing. A <code>MessagingException</code> holds a reference to the
 353  
      * current message that is passed into this method
 354  
      * 
 355  
      * @param message the current message being processed
 356  
      * @param e the top level exception thrown. This may be a Messaging exception or
 357  
      *            some wrapper exception
 358  
      * @see MessagingException
 359  
      */
 360  
     public abstract void handleMessagingException(UMOMessage message, Throwable e);
 361  
 
 362  
     /**
 363  
      * A routing exception is thrown when an excpetion occurs during normal message
 364  
      * processing A <code>RoutingException</code> holds a reference to the current
 365  
      * message and te endpoint being routing to or from when the error occurred. Both
 366  
      * are passed into this method
 367  
      * 
 368  
      * @param message the current message being processed
 369  
      * @param endpoint the endpoint being dispatched to or received from when the
 370  
      *            error occurred
 371  
      * @param e the top level exception thrown. This may be a Messaging exception or
 372  
      *            some wrapper exception
 373  
      * @see RoutingException
 374  
      */
 375  
     public abstract void handleRoutingException(UMOMessage message, UMOImmutableEndpoint endpoint, Throwable e);
 376  
 
 377  
     /**
 378  
      * Lifecycle exceptions are thrown when an error occurs during an object's
 379  
      * lifecycle call such as start, stop or initialise. The exception contains a
 380  
      * reference to the object that failed which can be used for more informative
 381  
      * logging.
 382  
      * 
 383  
      * @param component the object that failed during a lifecycle call
 384  
      * @param e the top level exception thrown. This may or may not be the
 385  
      *            <code>LifecycleException</code> but a lifecycle exception will be
 386  
      *            present in the exception stack.
 387  
      * @see LifecycleException
 388  
      */
 389  
     public abstract void handleLifecycleException(Object component, Throwable e);
 390  
 
 391  
     /**
 392  
      * A handler for all other exceptions
 393  
      * 
 394  
      * @param e the top level exception thrown
 395  
      */
 396  
     public abstract void handleStandardException(Throwable e);
 397  
 }