Coverage Report - org.mule.exception.AbstractExceptionListener
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractExceptionListener
0%
0/93
0%
0/60
0
AbstractExceptionListener$1
0%
0/2
N/A
0
 
 1  
 /*
 2  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 3  
  * The software in this package is published under the terms of the CPAL v1.0
 4  
  * license, a copy of which has been included with this distribution in the
 5  
  * LICENSE.txt file.
 6  
  */
 7  
 package org.mule.exception;
 8  
 
 9  
 import org.mule.DefaultMuleEvent;
 10  
 import org.mule.DefaultMuleMessage;
 11  
 import org.mule.api.MuleContext;
 12  
 import org.mule.api.MuleEvent;
 13  
 import org.mule.api.MuleException;
 14  
 import org.mule.api.MuleMessage;
 15  
 import org.mule.api.config.MuleProperties;
 16  
 import org.mule.api.construct.FlowConstruct;
 17  
 import org.mule.api.endpoint.EndpointURI;
 18  
 import org.mule.api.lifecycle.InitialisationException;
 19  
 import org.mule.api.processor.MessageProcessor;
 20  
 import org.mule.api.transaction.Transaction;
 21  
 import org.mule.api.transaction.TransactionException;
 22  
 import org.mule.api.util.StreamCloserService;
 23  
 import org.mule.config.ExceptionHelper;
 24  
 import org.mule.context.notification.ExceptionNotification;
 25  
 import org.mule.message.ExceptionMessage;
 26  
 import org.mule.processor.AbstractMessageProcessorOwner;
 27  
 import org.mule.routing.filters.WildcardFilter;
 28  
 import org.mule.routing.outbound.MulticastingRouter;
 29  
 import org.mule.transaction.TransactionCoordination;
 30  
 
 31  
 import java.util.List;
 32  
 import java.util.concurrent.CopyOnWriteArrayList;
 33  
 import java.util.concurrent.atomic.AtomicBoolean;
 34  
 
 35  
 import org.apache.commons.logging.Log;
 36  
 import org.apache.commons.logging.LogFactory;
 37  
 
 38  
 /**
 39  
  * <code>AbstractExceptionListener</code> is a base implementation that custom
 40  
  * Exception Listeners can override. It provides template methods for handling the
 41  
  * for base types of exceptions plus allows multiple targets to be associated with
 42  
  * this exception listener and provides an implementation for dispatching exception
 43  
  * events from this Listener.
 44  
  */
 45  0
 public abstract class AbstractExceptionListener extends AbstractMessageProcessorOwner
 46  
 {
 47  
     /**
 48  
      * logger used by this class
 49  
      */
 50  0
     protected transient Log logger = LogFactory.getLog(getClass());
 51  
 
 52  0
     @SuppressWarnings("unchecked")
 53  
     protected List<MessageProcessor> messageProcessors = new CopyOnWriteArrayList();
 54  
 
 55  0
     protected AtomicBoolean initialised = new AtomicBoolean(false);
 56  
 
 57  
     protected WildcardFilter rollbackTxFilter;
 58  
     protected WildcardFilter commitTxFilter;
 59  
 
 60  0
     protected boolean enableNotifications = true;
 61  
 
 62  
     public List<MessageProcessor> getMessageProcessors()
 63  
     {
 64  0
         return messageProcessors;
 65  
     }
 66  
 
 67  
     public void setMessageProcessors(List<MessageProcessor> processors)
 68  
     {
 69  0
         if (processors != null)
 70  
         {
 71  0
             this.messageProcessors.clear();
 72  0
             this.messageProcessors.addAll(processors);
 73  
         }
 74  
         else
 75  
         {
 76  0
             throw new IllegalArgumentException("List of targets = null");
 77  
         }
 78  0
     }
 79  
 
 80  
     public void addEndpoint(MessageProcessor processor)
 81  
     {
 82  0
         if (processor != null)
 83  
         {
 84  0
             messageProcessors.add(processor);
 85  
         }
 86  0
     }
 87  
 
 88  
     public boolean removeMessageProcessor(MessageProcessor processor)
 89  
     {
 90  0
         return messageProcessors.remove(processor);
 91  
     }
 92  
 
 93  
     protected Throwable getExceptionType(Throwable t, Class exceptionType)
 94  
     {
 95  0
         while (t != null)
 96  
         {
 97  0
             if (exceptionType.isAssignableFrom(t.getClass()))
 98  
             {
 99  0
                 return t;
 100  
             }
 101  
 
 102  0
             t = t.getCause();
 103  
         }
 104  
 
 105  0
         return null;
 106  
     }
 107  
 
 108  
     /**
 109  
      * The initialise method is call every time the Exception stategy is assigned to
 110  
      * a service or connector. This implementation ensures that initialise is called
 111  
      * only once. The actual initialisation code is contained in the
 112  
      * <code>doInitialise()</code> method.
 113  
      * 
 114  
      * @throws InitialisationException
 115  
      */
 116  
     public final synchronized void initialise() throws InitialisationException
 117  
     {
 118  0
         super.initialise();
 119  0
         if (!initialised.get())
 120  
         {
 121  0
             doInitialise(muleContext);
 122  0
             initialised.set(true);
 123  
         }
 124  0
     }
 125  
 
 126  
     protected void doInitialise(MuleContext muleContext) throws InitialisationException
 127  
     {
 128  0
         logger.info("Initialising exception listener: " + toString());
 129  0
     }
 130  
 
 131  
     /**
 132  
      * If there is a current transaction this method will mark it for rollback This
 133  
      * method should not be called if an event is routed from this exception handler
 134  
      * to an endpoint that should take part in the current transaction
 135  
      */
 136  
     protected void handleTransaction(Throwable t)
 137  
     {
 138  0
         Transaction tx = TransactionCoordination.getInstance().getTransaction();
 139  
 
 140  0
         if (tx == null)
 141  
         {
 142  0
             return;
 143  
         }
 144  
         // Work with the root exception, not anything thaat wraps it
 145  0
         t = ExceptionHelper.getRootException(t);
 146  
 
 147  0
         if (rollbackTxFilter == null && commitTxFilter == null)
 148  
         {
 149  
             // By default, rollback the transaction
 150  0
             rollbackTransaction();
 151  
         }
 152  0
         else if (rollbackTxFilter != null && rollbackTxFilter.accept(t.getClass().getName()))
 153  
         {
 154  
             // the rollback filter take preceedence over th ecommit filter
 155  0
             rollbackTransaction();
 156  
         }
 157  0
         else if (commitTxFilter != null && !commitTxFilter.accept(t.getClass().getName()))
 158  
         {
 159  
             // we only have to rollback if the commitTxFilter does NOT match
 160  0
             rollbackTransaction();
 161  
         }
 162  0
     }
 163  
 
 164  
     protected boolean isRollback(Throwable t)
 165  
     {
 166  
         // Work with the root exception, not anything thaat wraps it
 167  0
         t = ExceptionHelper.getRootException(t);
 168  0
         if (rollbackTxFilter == null && commitTxFilter == null)
 169  
         {
 170  0
             return true;
 171  
         }
 172  
         else
 173  
         {
 174  0
             return (rollbackTxFilter != null && rollbackTxFilter.accept(t.getClass().getName()))
 175  
                    || (commitTxFilter != null && !commitTxFilter.accept(t.getClass().getName()));
 176  
         }
 177  
     }
 178  
 
 179  
     protected void rollbackTransaction()
 180  
     {
 181  0
         Transaction tx = TransactionCoordination.getInstance().getTransaction();
 182  
         try
 183  
         {
 184  0
             if (tx != null)
 185  
             {
 186  0
                 tx.rollback();
 187  
             }
 188  
         }
 189  0
         catch (TransactionException e)
 190  
         {
 191  0
             logException(e);
 192  0
         }
 193  0
     }
 194  
 
 195  
     /**
 196  
      * Routes the current exception to an error endpoint such as a Dead Letter Queue
 197  
      * (jms) This method is only invoked if there is a MuleMessage available to
 198  
      * dispatch. The message dispatched from this method will be an
 199  
      * <code>ExceptionMessage</code> which contains the exception thrown the
 200  
      * MuleMessage and any context information.
 201  
      * 
 202  
      * @param message the MuleMessage being processed when the exception occurred
 203  
      * @param target optional; the endpoint being dispatched or received on
 204  
      *            when the error occurred. This is NOT the endpoint that the message
 205  
      *            will be disptched on and is only supplied to this method for
 206  
      *            logging purposes
 207  
      * @param t the exception thrown. This will be sent with the ExceptionMessage
 208  
      * @see ExceptionMessage
 209  
      */
 210  
     protected void routeException(MuleEvent event, MessageProcessor target, Throwable t)
 211  
     {
 212  0
         if (!messageProcessors.isEmpty())
 213  
         {
 214  
             try
 215  
             {
 216  0
                 logger.error("Message being processed is: " + (event.getMessage().getPayloadForLogging()));
 217  0
                 String component = "Unknown";
 218  0
                 if (event.getFlowConstruct() != null)
 219  
                 {
 220  0
                     component = event.getFlowConstruct().getName();
 221  
                 }
 222  0
                 EndpointURI endpointUri = event.getEndpoint().getEndpointURI();
 223  
 
 224  
                 // Create an ExceptionMessage which contains the original payload, the exception, and some additional context info.
 225  0
                 ExceptionMessage msg = new ExceptionMessage(event, t, component, endpointUri);
 226  0
                 MuleMessage exceptionMessage = new DefaultMuleMessage(msg, event.getMessage(), muleContext);
 227  
 
 228  
                 // Create an outbound router with all endpoints configured on the exception strategy
 229  0
                 MulticastingRouter router = new MulticastingRouter()
 230  0
                 {
 231  
                     @Override
 232  
                     protected void setMessageProperties(FlowConstruct session, MuleMessage message, MessageProcessor target)
 233  
                     {
 234  
                         // No reply-to or correlation for exception targets, at least for now anyway.
 235  0
                     }
 236  
                 };
 237  0
                 router.setRoutes(getMessageProcessors());
 238  0
                 router.setMuleContext(muleContext);
 239  
                 
 240  
                 // Route the ExceptionMessage to the new router
 241  0
                 router.process(new DefaultMuleEvent(exceptionMessage, event));
 242  
             }
 243  0
             catch (Exception e)
 244  
             {
 245  0
                 logFatal(event, e);
 246  0
             }
 247  
         }
 248  0
     }
 249  
 
 250  
     protected void closeStream(MuleMessage message)
 251  
     {
 252  0
         if (muleContext == null || muleContext.isDisposing() || muleContext.isDisposed())
 253  
         {
 254  0
             return;
 255  
         }
 256  0
         if (message != null
 257  
             && muleContext.getRegistry().lookupObject(MuleProperties.OBJECT_MULE_STREAM_CLOSER_SERVICE) != null)
 258  
         {
 259  0
             ((StreamCloserService) muleContext.getRegistry().lookupObject(
 260  
                     MuleProperties.OBJECT_MULE_STREAM_CLOSER_SERVICE)).closeStream(message.getPayload());
 261  
         }
 262  0
     }
 263  
 
 264  
     /**
 265  
      * Used to log the error passed into this Exception Listener
 266  
      * 
 267  
      * @param t the exception thrown
 268  
      */
 269  
     protected void logException(Throwable t)
 270  
     {
 271  0
         MuleException muleException = ExceptionHelper.getRootMuleException(t);
 272  0
         if (muleException != null)
 273  
         {
 274  0
             logger.error(muleException.getDetailedMessage());
 275  
         }
 276  
         else
 277  
         {
 278  0
             logger.error("Caught exception in Exception Strategy: " + t.getMessage(), t);
 279  
         }
 280  0
     }
 281  
 
 282  
     /**
 283  
      * Logs a fatal error message to the logging system. This should be used mostly
 284  
      * if an error occurs in the exception listener itself. This implementation logs
 285  
      * the the message itself to the logs if it is not null
 286  
      * 
 287  
      * @param message The MuleMessage currently being processed
 288  
      * @param t the fatal exception to log
 289  
      */
 290  
     protected void logFatal(MuleEvent event, Throwable t)
 291  
     {
 292  0
         logger.fatal(
 293  
             "Failed to dispatch message to error queue after it failed to process.  This may cause message loss."
 294  
                             + (event.getMessage() == null ? "" : "Logging Message here: \n" + event.getMessage().toString()), t);
 295  0
     }
 296  
 
 297  
     public boolean isInitialised()
 298  
     {
 299  0
         return initialised.get();
 300  
     }
 301  
 
 302  
     /**
 303  
      * Fires a server notification to all registered
 304  
      * {@link org.mule.api.context.notification.ExceptionNotificationListener}
 305  
      * eventManager.
 306  
      * 
 307  
      * @param notification the notification to fire.
 308  
      */
 309  
     protected void fireNotification(ExceptionNotification notification)
 310  
     {
 311  0
         if (muleContext != null)
 312  
         {
 313  0
             muleContext.fireNotification(notification);
 314  
         }
 315  0
         else if (logger.isWarnEnabled())
 316  
         {
 317  0
             logger.debug("MuleContext is not yet available for firing notifications, ignoring event: "
 318  
                          + notification);
 319  
         }
 320  0
     }
 321  
 
 322  
     public WildcardFilter getCommitTxFilter()
 323  
     {
 324  0
         return commitTxFilter;
 325  
     }
 326  
 
 327  
     public void setCommitTxFilter(WildcardFilter commitTxFilter)
 328  
     {
 329  0
         this.commitTxFilter = commitTxFilter;
 330  0
     }
 331  
 
 332  
     public boolean isEnableNotifications()
 333  
     {
 334  0
         return enableNotifications;
 335  
     }
 336  
 
 337  
     public void setEnableNotifications(boolean enableNotifications)
 338  
     {
 339  0
         this.enableNotifications = enableNotifications;
 340  0
     }
 341  
 
 342  
     public WildcardFilter getRollbackTxFilter()
 343  
     {
 344  0
         return rollbackTxFilter;
 345  
     }
 346  
 
 347  
     public void setRollbackTxFilter(WildcardFilter rollbackTxFilter)
 348  
     {
 349  0
         this.rollbackTxFilter = rollbackTxFilter;
 350  0
     }
 351  
     
 352  
     @Override
 353  
     protected List<MessageProcessor> getOwnedMessageProcessors()
 354  
     {
 355  0
         return messageProcessors;
 356  
     }
 357  
 }