Coverage Report - org.mule.processor.SedaStageInterceptingMessageProcessor
 
Classes in this File Line Coverage Branch Coverage Complexity
SedaStageInterceptingMessageProcessor
0%
0/99
0%
0/60
0
SedaStageInterceptingMessageProcessor$1
0%
0/8
0%
0/4
0
SedaStageInterceptingMessageProcessor$2
0%
0/9
0%
0/2
0
SedaStageInterceptingMessageProcessor$3
0%
0/6
N/A
0
SedaStageInterceptingMessageProcessor$4
0%
0/3
N/A
0
SedaStageInterceptingMessageProcessor$SedaStageWorker
0%
0/12
0%
0/2
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.processor;
 8  
 
 9  
 import org.mule.DefaultMuleEvent;
 10  
 import org.mule.OptimizedRequestContext;
 11  
 import org.mule.api.MessagingException;
 12  
 import org.mule.api.MuleContext;
 13  
 import org.mule.api.MuleEvent;
 14  
 import org.mule.api.MuleException;
 15  
 import org.mule.api.NamedObject;
 16  
 import org.mule.api.config.ThreadingProfile;
 17  
 import org.mule.api.context.WorkManager;
 18  
 import org.mule.api.exception.MessagingExceptionHandler;
 19  
 import org.mule.api.exception.SystemExceptionHandler;
 20  
 import org.mule.api.lifecycle.InitialisationException;
 21  
 import org.mule.api.lifecycle.Lifecycle;
 22  
 import org.mule.api.lifecycle.LifecycleCallback;
 23  
 import org.mule.api.lifecycle.LifecycleException;
 24  
 import org.mule.api.processor.MessageProcessor;
 25  
 import org.mule.api.service.FailedToQueueEventException;
 26  
 import org.mule.config.QueueProfile;
 27  
 import org.mule.config.i18n.CoreMessages;
 28  
 import org.mule.config.i18n.MessageFactory;
 29  
 import org.mule.lifecycle.EmptyLifecycleCallback;
 30  
 import org.mule.management.stats.QueueStatistics;
 31  
 import org.mule.service.Pausable;
 32  
 import org.mule.service.Resumable;
 33  
 import org.mule.util.concurrent.WaitableBoolean;
 34  
 import org.mule.util.queue.Queue;
 35  
 import org.mule.util.queue.QueueSession;
 36  
 import org.mule.work.AbstractMuleEventWork;
 37  
 import org.mule.work.MuleWorkManager;
 38  
 
 39  
 import java.text.MessageFormat;
 40  
 
 41  
 import javax.resource.spi.work.Work;
 42  
 import javax.resource.spi.work.WorkException;
 43  
 
 44  
 /**
 45  
  * Processes {@link MuleEvent}'s asynchronously using a {@link MuleWorkManager} to
 46  
  * schedule asynchronous processing of the next {@link MessageProcessor}.
 47  
  */
 48  0
 public class SedaStageInterceptingMessageProcessor extends OptionalAsyncInterceptingMessageProcessor
 49  
     implements Work, Lifecycle, Pausable, Resumable
 50  
 {
 51  
     protected static final String QUEUE_NAME_PREFIX = "seda.queue";
 52  
 
 53  
     protected QueueProfile queueProfile;
 54  
     protected int queueTimeout;
 55  
     protected QueueStatistics queueStatistics;
 56  
     protected MuleContext muleContext;
 57  
     protected String name;
 58  
     protected Queue queue;
 59  0
     private WaitableBoolean running = new WaitableBoolean(false);;
 60  
     protected SedaStageLifecycleManager lifecycleManager;
 61  
 
 62  
     public SedaStageInterceptingMessageProcessor(String name,
 63  
                                                  QueueProfile queueProfile,
 64  
                                                  int queueTimeout,
 65  
                                                  ThreadingProfile threadingProfile,
 66  
                                                  QueueStatistics queueStatistics,
 67  
                                                  MuleContext muleContext)
 68  
     {
 69  0
         super(threadingProfile, "seda." + name, muleContext.getConfiguration().getShutdownTimeout());
 70  0
         this.name = name;
 71  0
         this.queueProfile = queueProfile;
 72  0
         this.queueTimeout = queueTimeout;
 73  0
         this.queueStatistics = queueStatistics;
 74  0
         this.muleContext = muleContext;
 75  0
         lifecycleManager = new SedaStageLifecycleManager(name, this);
 76  0
     }
 77  
 
 78  
     @Override
 79  
     protected void processNextAsync(MuleEvent event) throws MuleException
 80  
     {
 81  
         try
 82  
         {
 83  0
             if (isStatsEnabled())
 84  
             {
 85  0
                 queueStatistics.incQueuedEvent();
 86  
             }
 87  0
             enqueue(event);
 88  
         }
 89  0
         catch (Exception e)
 90  
         {
 91  0
             throw new FailedToQueueEventException(
 92  
                 CoreMessages.interruptedQueuingEventFor(getStageDescription()), event, e);
 93  0
         }
 94  
 
 95  0
         if (logger.isTraceEnabled())
 96  
         {
 97  0
             logger.trace("MuleEvent added to queue for: " + getStageDescription());
 98  
         }
 99  0
     }
 100  
 
 101  
     protected boolean isStatsEnabled()
 102  
     {
 103  0
         return queueStatistics != null && queueStatistics.isEnabled();
 104  
     }
 105  
 
 106  
     protected void enqueue(MuleEvent event) throws Exception
 107  
     {
 108  0
         if (logger.isDebugEnabled())
 109  
         {
 110  0
             logger.debug(MessageFormat.format("{1}: Putting event on queue {2}", queue.getName(),
 111  
                 getStageDescription(), event));
 112  
         }
 113  0
         queue.put(event);
 114  0
     }
 115  
 
 116  
     protected MuleEvent dequeue() throws Exception
 117  
     {
 118  0
         if (queue == null)
 119  
         {
 120  0
             return null;
 121  
         }
 122  0
         if (logger.isTraceEnabled())
 123  
         {
 124  0
             logger.trace(MessageFormat.format("{0}: Polling queue {1}, timeout = {2}", getStageName(),
 125  
                 getStageDescription(), queueTimeout));
 126  
         }
 127  
 
 128  0
         MuleEvent event = (MuleEvent) queue.poll(queueTimeout);
 129  
         // If the service has been paused why the poll was waiting for an event to
 130  
         // arrive on the queue,
 131  
         // we put the object back on the queue
 132  0
         if (event != null && lifecycleManager.isPhaseComplete(Pausable.PHASE_NAME))
 133  
         {
 134  0
             queue.untake(event);
 135  0
             return null;
 136  
         }
 137  0
         return event;
 138  
     }
 139  
 
 140  
     private class SedaStageWorker extends AbstractMuleEventWork
 141  
     {
 142  
         public SedaStageWorker(MuleEvent event)
 143  0
         {
 144  0
             super(event);
 145  0
         }
 146  
 
 147  
         @Override
 148  
         protected void doRun()
 149  
         {
 150  
             try
 151  
             {
 152  0
                 processNextTimed(event);
 153  
             }
 154  0
             catch (Exception e)
 155  
             {
 156  0
                 event.getSession().setValid(false);
 157  0
                 MessagingExceptionHandler exceptionListener = event.getFlowConstruct().getExceptionListener();
 158  0
                 if (e instanceof MessagingException)
 159  
                 {
 160  0
                     exceptionListener.handleException(e, event);
 161  
                 }
 162  
                 else
 163  
                 {
 164  0
                     exceptionListener.handleException(
 165  
                         new MessagingException(CoreMessages.eventProcessingFailedFor(getStageDescription()),
 166  
                             event, e), event);
 167  
                 }
 168  0
             }
 169  0
         }
 170  
     }
 171  
 
 172  
     /**
 173  
      * While the service isn't stopped this runs a continuous loop checking for new
 174  
      * events in the queue.
 175  
      */
 176  
     public void run()
 177  
     {
 178  0
         DefaultMuleEvent event = null;
 179  0
         QueueSession queueSession = muleContext.getQueueManager().getQueueSession();
 180  
 
 181  0
         running.set(true);
 182  0
         while (!lifecycleManager.getState().isStopped())
 183  
         {
 184  
             try
 185  
             {
 186  
                 // Wait if the service is paused
 187  0
                 if (lifecycleManager.isPhaseComplete(Pausable.PHASE_NAME))
 188  
                 {
 189  0
                     waitIfPaused();
 190  
 
 191  
                     // If service is resumed as part of stopping
 192  0
                     if (lifecycleManager.getState().isStopping())
 193  
                     {
 194  0
                         if (!isQueuePersistent() && (queueSession != null && getQueueSize() > 0))
 195  
                         {
 196  
                             // Any messages in a non-persistent queue went paused
 197  
                             // service is stopped are lost
 198  0
                             logger.warn(CoreMessages.stopPausedSedaStageNonPeristentQueueMessageLoss(
 199  
                                 getQueueSize(), getQueueName()));
 200  
                         }
 201  0
                         break;
 202  
                     }
 203  
                 }
 204  
 
 205  
                 // If we're doing a draining stop, read all events from the queue
 206  
                 // before stopping
 207  0
                 if (lifecycleManager.getState().isStopping())
 208  
                 {
 209  0
                     if (isQueuePersistent() || queueSession == null || getQueueSize() <= 0)
 210  
                     {
 211  0
                         break;
 212  
                     }
 213  
                 }
 214  
 
 215  0
                 event = (DefaultMuleEvent) dequeue();
 216  
             }
 217  0
             catch (InterruptedException ie)
 218  
             {
 219  0
                 break;
 220  
             }
 221  0
             catch (Exception e)
 222  
             {
 223  0
                 SystemExceptionHandler exceptionListener = muleContext.getExceptionListener();
 224  0
                 if (e instanceof MuleException)
 225  
                 {
 226  0
                     exceptionListener.handleException(e);
 227  
                 }
 228  
                 else
 229  
                 {
 230  0
                     exceptionListener.handleException(new MessagingException(
 231  
                         CoreMessages.eventProcessingFailedFor(getStageDescription()), event, e));
 232  
                 }
 233  0
             }
 234  
 
 235  0
             if (event != null)
 236  
             {
 237  0
                 if (isStatsEnabled())
 238  
                 {
 239  0
                     queueStatistics.decQueuedEvent();
 240  
                 }
 241  
 
 242  0
                 if (logger.isDebugEnabled())
 243  
                 {
 244  0
                     logger.debug(MessageFormat.format("{0}: Dequeued event from {1}", getStageDescription(),
 245  
                         getQueueName()));
 246  
                 }
 247  0
                 AbstractMuleEventWork work = new SedaStageWorker(event);
 248  0
                 if (doThreading)
 249  
                 {
 250  
                     try
 251  
                     {
 252  0
                         workManagerSource.getWorkManager().scheduleWork(work, WorkManager.INDEFINITE, null,
 253  
                             new AsyncWorkListener(next));
 254  
                     }
 255  0
                     catch (Exception e)
 256  
                     {
 257  
                         // Use the event copy created in SedaStageWorker constructor
 258  
                         // because dequeued event may still be owned by a previuos
 259  
                         // thread
 260  0
                         OptimizedRequestContext.unsafeSetEvent(work.getEvent());
 261  0
                         event.getFlowConstruct().getExceptionListener().handleException(e, work.getEvent());
 262  0
                     }
 263  
                 }
 264  
                 else
 265  
                 {
 266  0
                     work.run();
 267  
                 }
 268  0
             }
 269  
         }
 270  0
         running.set(false);
 271  0
     }
 272  
 
 273  
     /** Are the events in the SEDA queue persistent? */
 274  
     protected boolean isQueuePersistent()
 275  
     {
 276  0
         return queueProfile.isPersistent();
 277  
     }
 278  
 
 279  
     public int getQueueSize()
 280  
     {
 281  0
         return queue.size();
 282  
     }
 283  
 
 284  
     protected String getQueueName()
 285  
     {
 286  0
         return String.format("%s(%s)", QUEUE_NAME_PREFIX, getStageName());
 287  
     }
 288  
 
 289  
     protected String getStageName()
 290  
     {
 291  0
         if (name != null)
 292  
         {
 293  0
             return name;
 294  
         }
 295  0
         else if (next instanceof NamedObject)
 296  
         {
 297  0
             return ((NamedObject) next).getName();
 298  
         }
 299  
         else
 300  
         {
 301  0
             return String.format("%s.%s", next.getClass().getName(), next.hashCode());
 302  
         }
 303  
     }
 304  
 
 305  
     protected String getStageDescription()
 306  
     {
 307  0
         return "SEDA Stage " + getStageName();
 308  
     }
 309  
 
 310  
     protected void waitIfPaused() throws InterruptedException
 311  
     {
 312  0
         if (logger.isDebugEnabled() && lifecycleManager.isPhaseComplete(Pausable.PHASE_NAME))
 313  
         {
 314  0
             logger.debug(getStageDescription() + " is paused. Polling halted until resumed is called");
 315  
         }
 316  
         while (lifecycleManager.isPhaseComplete(Pausable.PHASE_NAME)
 317  0
                && !lifecycleManager.getState().isStopping())
 318  
         {
 319  0
             Thread.sleep(50);
 320  
         }
 321  0
     }
 322  
 
 323  
     public void release()
 324  
     {
 325  0
         running.set(false);
 326  0
     }
 327  
 
 328  
     public void initialise() throws InitialisationException
 329  
     {
 330  0
         lifecycleManager.fireInitialisePhase(new LifecycleCallback<SedaStageInterceptingMessageProcessor>()
 331  0
         {
 332  
             public void onTransition(String phaseName, SedaStageInterceptingMessageProcessor object)
 333  
                 throws MuleException
 334  
             {
 335  0
                 if (next == null)
 336  
                 {
 337  0
                     throw new IllegalStateException(
 338  
                         "Next message processor cannot be null with this InterceptingMessageProcessor");
 339  
                 }
 340  
                 // Setup event Queue
 341  0
                 queueProfile.configureQueue(getQueueName(), muleContext.getQueueManager());
 342  0
                 queue = muleContext.getQueueManager().getQueueSession().getQueue(getQueueName());
 343  0
                 if (queue == null)
 344  
                 {
 345  0
                     throw new InitialisationException(
 346  
                         MessageFactory.createStaticMessage("Queue not created for " + getStageDescription()),
 347  
                         SedaStageInterceptingMessageProcessor.this);
 348  
                 }
 349  0
             }
 350  
         });
 351  0
     }
 352  
 
 353  
     @Override
 354  
     public void start() throws MuleException
 355  
     {
 356  0
         lifecycleManager.fireStartPhase(new LifecycleCallback<SedaStageInterceptingMessageProcessor>()
 357  0
         {
 358  
             public void onTransition(String phaseName, SedaStageInterceptingMessageProcessor object)
 359  
                 throws MuleException
 360  
             {
 361  0
                 if (queue == null)
 362  
                 {
 363  0
                     throw new IllegalStateException("Not initialised");
 364  
                 }
 365  0
                 SedaStageInterceptingMessageProcessor.super.start();
 366  
                 try
 367  
                 {
 368  0
                     workManagerSource.getWorkManager().scheduleWork(
 369  
                         SedaStageInterceptingMessageProcessor.this, WorkManager.INDEFINITE, null,
 370  
                         new AsyncWorkListener(next));
 371  
                 }
 372  0
                 catch (WorkException e)
 373  
                 {
 374  0
                     throw new LifecycleException(CoreMessages.failedToStart(getStageDescription()), e, this);
 375  
 
 376  0
                 }
 377  0
             }
 378  
         });
 379  0
     }
 380  
 
 381  
     @Override
 382  
     public void stop() throws MuleException
 383  
     {
 384  0
         lifecycleManager.fireStopPhase(new LifecycleCallback<SedaStageInterceptingMessageProcessor>()
 385  0
         {
 386  
             public void onTransition(String phaseName, SedaStageInterceptingMessageProcessor object)
 387  
                 throws MuleException
 388  
             {
 389  
                 try
 390  
                 {
 391  0
                     running.whenFalse(null);
 392  
                 }
 393  0
                 catch (InterruptedException e)
 394  
                 {
 395  
                     // we can ignore this
 396  0
                 }
 397  0
                 SedaStageInterceptingMessageProcessor.super.stop();
 398  0
             }
 399  
         });
 400  0
     }
 401  
 
 402  
     public void dispose()
 403  
     {
 404  0
         lifecycleManager.fireDisposePhase(new LifecycleCallback<SedaStageInterceptingMessageProcessor>()
 405  0
         {
 406  
             public void onTransition(String phaseName, SedaStageInterceptingMessageProcessor object)
 407  
                 throws MuleException
 408  
             {
 409  0
                 queue = null;
 410  0
             }
 411  
         });
 412  0
     }
 413  
 
 414  
     public void pause() throws MuleException
 415  
     {
 416  0
         lifecycleManager.firePausePhase(new EmptyLifecycleCallback<SedaStageInterceptingMessageProcessor>());
 417  0
     }
 418  
 
 419  
     public void resume() throws MuleException
 420  
     {
 421  0
         lifecycleManager.fireResumePhase(new EmptyLifecycleCallback<SedaStageInterceptingMessageProcessor>());
 422  0
     }
 423  
 
 424  
 }