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