Coverage Report - org.mule.model.seda.SedaService
 
Classes in this File Line Coverage Branch Coverage Complexity
SedaService
43%
68/159
33%
28/86
2.781
SedaService$ComponentStageWorker
0%
0/17
0%
0/2
2.781
 
 1  
 /*
 2  
  * $Id: SedaService.java 12269 2008-07-10 04:19:03Z dfeist $
 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.model.seda;
 12  
 
 13  
 import org.mule.DefaultMuleEvent;
 14  
 import org.mule.DefaultMuleMessage;
 15  
 import org.mule.FailedToQueueEventException;
 16  
 import org.mule.OptimizedRequestContext;
 17  
 import org.mule.RequestContext;
 18  
 import org.mule.api.ExceptionPayload;
 19  
 import org.mule.api.MessagingException;
 20  
 import org.mule.api.MuleEvent;
 21  
 import org.mule.api.MuleException;
 22  
 import org.mule.api.MuleMessage;
 23  
 import org.mule.api.MuleRuntimeException;
 24  
 import org.mule.api.config.ThreadingProfile;
 25  
 import org.mule.api.context.WorkManager;
 26  
 import org.mule.api.endpoint.InboundEndpoint;
 27  
 import org.mule.api.lifecycle.InitialisationException;
 28  
 import org.mule.api.lifecycle.LifecycleException;
 29  
 import org.mule.api.service.ServiceException;
 30  
 import org.mule.api.transport.ReplyToHandler;
 31  
 import org.mule.config.QueueProfile;
 32  
 import org.mule.config.i18n.CoreMessages;
 33  
 import org.mule.config.i18n.MessageFactory;
 34  
 import org.mule.management.stats.ServiceStatistics;
 35  
 import org.mule.message.DefaultExceptionPayload;
 36  
 import org.mule.service.AbstractService;
 37  
 import org.mule.transport.NullPayload;
 38  
 import org.mule.util.queue.Queue;
 39  
 import org.mule.util.queue.QueueSession;
 40  
 
 41  
 import java.util.NoSuchElementException;
 42  
 
 43  
 import javax.resource.spi.work.Work;
 44  
 import javax.resource.spi.work.WorkEvent;
 45  
 import javax.resource.spi.work.WorkException;
 46  
 import javax.resource.spi.work.WorkListener;
 47  
 
 48  
 /**
 49  
  * A Seda service runs inside a Seda Model and is responsible for managing a Seda
 50  
  * Queue and thread pool for a Mule sevice service. In Seda terms this is
 51  
  * equivilent to a stage.
 52  
  */
 53  0
 public class SedaService extends AbstractService implements Work, WorkListener
 54  
 {
 55  
     /**
 56  
      * Serial version/
 57  
      */
 58  
     private static final long serialVersionUID = 7711976708670893015L;
 59  
     
 60  
     private static final String QUEUE_NAME_SUFFIX = ".component";
 61  
 
 62  
     protected WorkManager workManager;
 63  
 
 64  
     /**
 65  
      * The time out used for taking from the Seda Queue.
 66  
      */
 67  
     protected Integer queueTimeout;
 68  
 
 69  
     /**
 70  
      * The threading profile to use for this service. If this is not set a default
 71  
      * will be provided by the server
 72  
      */
 73  
     protected ThreadingProfile threadingProfile;
 74  
 
 75  
     /**
 76  
      * The queue profile to use for this service. If this is not set a default
 77  
      * will be provided by the server
 78  
      */
 79  
     protected QueueProfile queueProfile;
 80  
     
 81  
     protected Queue queue;
 82  
 
 83  
     /** For Spring only */
 84  
     public SedaService()
 85  
     {
 86  394
         super();
 87  394
     }
 88  
     
 89  
     /**
 90  
      * Initialise the service. The service will first create a Mule UMO from the
 91  
      * UMODescriptor and then initialise a pool based on the attributes in the
 92  
      * UMODescriptor.
 93  
      * 
 94  
      * @throws org.mule.api.lifecycle.InitialisationException if the service fails
 95  
      *             to initialise
 96  
      * @see org.mule.api.UMODescriptor
 97  
      */
 98  
     protected synchronized void doInitialise() throws InitialisationException
 99  
     {
 100  390
         if (threadingProfile == null)
 101  
         {
 102  
             // TODO MULE-2102 This should be configured in the default template.
 103  390
             threadingProfile = muleContext.getDefaultComponentThreadingProfile();
 104  
         }
 105  
         // Create thread pool
 106  390
         workManager = threadingProfile.createWorkManager(getName());
 107  
 
 108  390
         if (queueProfile == null)
 109  
         {
 110  
             // TODO MULE-2102 This should be configured in the default template.
 111  386
             queueProfile = ((SedaModel) model).getQueueProfile();
 112  
         }
 113  
         
 114  390
         if (queueTimeout == null)
 115  
         {
 116  
             // TODO MULE-2102 This should be configured in the default template.
 117  390
             setQueueTimeout(new Integer(((SedaModel) model).getQueueTimeout()));
 118  
         }
 119  
         
 120  
         try
 121  
         {
 122  390
             if (name == null)
 123  
             {
 124  0
                 throw new InitialisationException(MessageFactory.createStaticMessage("Service has no name to identify it"), this);
 125  
             }
 126  
             // Setup event Queue (used for VM execution).  The queue has the same name as the service.
 127  390
             queueProfile.configureQueue(name, muleContext.getQueueManager());
 128  390
             queue = muleContext.getQueueManager().getQueueSession().getQueue(name + QUEUE_NAME_SUFFIX);
 129  390
             if (queue == null)
 130  
             {
 131  0
                 throw new InitialisationException(MessageFactory.createStaticMessage("Queue not created for service " + name), this);
 132  
             }
 133  
         }
 134  0
         catch (InitialisationException e)
 135  
         {
 136  0
             throw e;
 137  
         }
 138  0
         catch (Throwable e)
 139  
         {
 140  0
             throw new InitialisationException(
 141  
                 CoreMessages.objectFailedToInitialise("Service Queue"), e, this);
 142  390
         }
 143  390
     }
 144  
 
 145  
     protected void doForceStop() throws MuleException
 146  
     {
 147  0
         doStop();
 148  0
     }
 149  
 
 150  
     protected void doStop() throws MuleException
 151  
     {
 152  14
         if (queue != null && queue.size() > 0)
 153  
         {
 154  
             try
 155  
             {
 156  0
                 stopping.whenFalse(null);
 157  
             }
 158  0
             catch (InterruptedException e)
 159  
             {
 160  
                 // we can ignore this
 161  
                 // TODO MULE-863: Why?
 162  0
             }
 163  
         }
 164  14
         workManager.dispose();
 165  14
     }
 166  
 
 167  
     protected void doStart() throws MuleException
 168  
     {
 169  
         try
 170  
         {
 171  14
             workManager.start();
 172  14
             workManager.scheduleWork(this, WorkManager.INDEFINITE, null, this);
 173  
         }
 174  0
         catch (Exception e)
 175  
         {
 176  0
             throw new LifecycleException(
 177  
                 CoreMessages.failedToStart("Service: " + name), e, this);
 178  14
         }
 179  14
     }
 180  
 
 181  
     protected void doDispose()
 182  
     {
 183  2
         queue = null;
 184  
         // threadPool.awaitTerminationAfterShutdown();
 185  2
         if (workManager != null)
 186  
         {
 187  2
             workManager.dispose();
 188  
         }
 189  2
     }
 190  
 
 191  
     protected void doDispatch(MuleEvent event) throws MuleException
 192  
     {
 193  
         // Dispatching event to the service
 194  0
         if (stats.isEnabled())
 195  
         {
 196  0
             stats.incReceivedEventASync();
 197  
         }
 198  0
         if (logger.isDebugEnabled())
 199  
         {
 200  0
             logger.debug("Service: " + name + " has received asynchronous event on: "
 201  
                          + event.getEndpoint().getEndpointURI());
 202  
         }
 203  
 
 204  
         // Block until we can queue the next event
 205  
         try
 206  
         {
 207  0
             enqueue(event);
 208  0
             if (stats.isEnabled())
 209  
             {
 210  0
                 stats.incQueuedEvent();
 211  
             }
 212  
         }
 213  0
         catch (Exception e)
 214  
         {
 215  0
             FailedToQueueEventException e1 = new FailedToQueueEventException(
 216  
                 CoreMessages.interruptedQueuingEventFor(this.getName()), event.getMessage(), this, e);
 217  0
             handleException(e1);
 218  0
         }
 219  
 
 220  0
         if (logger.isTraceEnabled())
 221  
         {
 222  0
             logger.trace("MuleEvent added to queue for: " + name);
 223  
         }
 224  0
     }
 225  
 
 226  
     protected MuleMessage doSend(MuleEvent event) throws MuleException
 227  
     {
 228  0
         MuleMessage result = null;
 229  
         try
 230  
         {
 231  0
             if (logger.isDebugEnabled())
 232  
             {
 233  0
                 logger.debug(this + " : got proxy for " + event.getId() + " = " + component);
 234  
             }
 235  0
             Object replyTo = event.getMessage().getReplyTo();
 236  0
             ReplyToHandler replyToHandler = getReplyToHandler(event.getMessage(), (InboundEndpoint) event.getEndpoint());
 237  0
             result = component.onCall(event);
 238  0
             result = sendToOutboundRouter(event, result);
 239  0
             result = processAsyncReplyRouter(result);
 240  0
             processReplyTo(event, result, replyToHandler, replyTo);
 241  
             // stats
 242  0
             if (stats.isEnabled())
 243  
             {
 244  0
                 stats.incSentEventSync();
 245  
             }
 246  
         }
 247  0
         catch (Exception e)
 248  
         {
 249  0
             event.getSession().setValid(false);
 250  0
             if (e instanceof MessagingException)
 251  
             {
 252  0
                 handleException(e);
 253  
             }
 254  
             else
 255  
             {
 256  0
                 handleException(new MessagingException(CoreMessages.eventProcessingFailedFor(getName()),
 257  
                     event.getMessage(), e));
 258  
             }
 259  0
             if (result == null)
 260  
             {
 261  
                 // important that we pull event from request context here as it may
 262  
                 // have been modified
 263  
                 // (necessary to avoid scribbling between threads)
 264  0
                 result = new DefaultMuleMessage(NullPayload.getInstance(), RequestContext.getEvent().getMessage());
 265  
             }
 266  0
             ExceptionPayload exceptionPayload = result.getExceptionPayload();
 267  0
             if (exceptionPayload == null)
 268  
             {
 269  0
                 exceptionPayload = new DefaultExceptionPayload(e);
 270  
             }
 271  0
             result.setExceptionPayload(exceptionPayload);
 272  0
         }
 273  0
         return result;
 274  
     }
 275  
 
 276  
     public int getQueueSize()
 277  
     {
 278  5
         if (queue == null)
 279  
         {
 280  0
             logger.warn(new InitialisationException(MessageFactory.createStaticMessage("Queue not created for service " + name), this));
 281  0
             return -1;
 282  
         }
 283  5
         return queue.size();
 284  
     }
 285  
 
 286  
     /**
 287  
      * While the service isn't stopped this runs a continuous loop checking for new
 288  
      * events in the queue.
 289  
      */
 290  
     public void run()
 291  
     {
 292  14
         DefaultMuleEvent event = null;
 293  14
         QueueSession queueSession = muleContext.getQueueManager().getQueueSession();
 294  
 
 295  33
         while (!stopped.get())
 296  
         {
 297  
             try
 298  
             {
 299  
                 // Wait if the service is paused
 300  33
                 paused.whenFalse(null);
 301  
 
 302  
                 // If we're doing a draining stop, read all events from the queue
 303  
                 // before stopping
 304  33
                 if (stopping.get())
 305  
                 {
 306  5
                     if (queueSession == null || getQueueSize() <= 0)
 307  
                     {
 308  5
                         stopping.set(false);
 309  
                         break;
 310  
                     }
 311  
                 }
 312  
 
 313  28
                 event = (DefaultMuleEvent) dequeue();
 314  19
                 if (event != null)
 315  
                 {
 316  0
                     if (stats.isEnabled())
 317  
                     {
 318  0
                         stats.decQueuedEvent();
 319  
                     }
 320  
 
 321  0
                     if (logger.isDebugEnabled())
 322  
                     {
 323  0
                         logger.debug("Service: " + name + " dequeued event on: "
 324  
                                         + event.getEndpoint().getEndpointURI());
 325  
                     }
 326  0
                     workManager.scheduleWork(new ComponentStageWorker(event), WorkManager.INDEFINITE, null, this);
 327  
                 }
 328  
             }
 329  9
             catch (Exception e)
 330  
             {
 331  9
                 if (isStopped() || isStopping())
 332  
                 {
 333  
                     break;
 334  
                 }
 335  
 
 336  1
                 if (e instanceof InterruptedException)
 337  
                 {
 338  1
                     stopping.set(false);
 339  
                     break;
 340  
                 }
 341  0
                 else if (e instanceof NoSuchElementException)
 342  
                 {
 343  0
                     handleException(new ServiceException(CoreMessages.proxyPoolTimedOut(),
 344  
                         (event == null ? null : event.getMessage()), this, e));
 345  
                 }
 346  0
                 else if (e instanceof MuleException)
 347  
                 {
 348  0
                     handleException(e);
 349  
                 }
 350  0
                 else if (e instanceof WorkException)
 351  
                 {
 352  0
                     handleException(
 353  
                         new ServiceException(
 354  
                             CoreMessages.eventProcessingFailedFor(name),
 355  
                             (event == null ? null : event.getMessage()), this, e));
 356  
                 }
 357  
                 else
 358  
                 {
 359  0
                     handleException(
 360  
                         new ServiceException(
 361  
                             CoreMessages.failedToGetPooledObject(),
 362  
                             (event == null ? null : event.getMessage()), this, e));
 363  
                 }
 364  
             }
 365  
             finally
 366  
             {
 367  33
                 stopping.set(false);
 368  19
             }
 369  
         }
 370  14
     }
 371  
 
 372  
     public void release()
 373  
     {
 374  0
         stopping.set(false);
 375  0
     }
 376  
 
 377  
     protected void enqueue(MuleEvent event) throws Exception
 378  
     {
 379  0
         if (queue == null)
 380  
         {
 381  0
             throw new InitialisationException(MessageFactory.createStaticMessage("Queue not created for service " + name), this);
 382  
         }
 383  0
         if (logger.isDebugEnabled())
 384  
         {
 385  0
             logger.debug("Service " + name + " putting event on queue " + queue.getName() + ": " + event);
 386  
         }
 387  0
         queue.put(event);
 388  0
     }
 389  
 
 390  
     protected MuleEvent dequeue() throws Exception
 391  
     {
 392  28
         if (queue == null)
 393  
         {
 394  0
             throw new InitialisationException(MessageFactory.createStaticMessage("Queue not created for service " + name), this);
 395  
         }
 396  28
         if (logger.isDebugEnabled())
 397  
         {
 398  0
             logger.debug("Service " + name + " polling queue " + queue.getName() + ", timeout = " + queueTimeout);
 399  
         }
 400  28
         if (getQueueTimeout() == null)
 401  
         {
 402  0
             throw new InitialisationException(CoreMessages.noServiceQueueTimeoutSet(this), this);
 403  
         }
 404  
         else
 405  
         {
 406  28
             return (MuleEvent) queue.poll(getQueueTimeout().intValue());
 407  
         }
 408  
     }
 409  
 
 410  
     public void workAccepted(WorkEvent event)
 411  
     {
 412  14
         handleWorkException(event, "workAccepted");
 413  14
     }
 414  
 
 415  
     public void workRejected(WorkEvent event)
 416  
     {
 417  0
         handleWorkException(event, "workRejected");
 418  0
     }
 419  
 
 420  
     public void workStarted(WorkEvent event)
 421  
     {
 422  14
         handleWorkException(event, "workStarted");
 423  14
     }
 424  
 
 425  
     public void workCompleted(WorkEvent event)
 426  
     {
 427  14
         handleWorkException(event, "workCompleted");
 428  14
     }
 429  
 
 430  
     protected void handleWorkException(WorkEvent event, String type)
 431  
     {
 432  
         Throwable e;
 433  
 
 434  44
         if (event != null && event.getException() != null)
 435  
         {
 436  2
             e = event.getException();
 437  
         }
 438  
         else
 439  
         {
 440  42
             return;
 441  
         }
 442  
 
 443  2
         if (event.getException().getCause() != null)
 444  
         {
 445  2
             e = event.getException().getCause();
 446  
         }
 447  
 
 448  2
         logger.error("Work caused exception on '" + type + "'. Work being executed was: "
 449  
                         + event.getWork().toString());
 450  
 
 451  2
         if (e instanceof Exception)
 452  
         {
 453  0
             handleException((Exception) e);
 454  
         }
 455  
         else
 456  
         {
 457  2
             throw new MuleRuntimeException(
 458  
                 CoreMessages.componentCausedErrorIs(this.getName()), e);
 459  
         }
 460  0
     }
 461  
 
 462  
     protected ServiceStatistics createStatistics()
 463  
     {
 464  390
         return new ServiceStatistics(getName(), threadingProfile.getMaxThreadsActive());
 465  
     }
 466  
 
 467  
     public Object getInstance() throws MuleException
 468  
     {
 469  0
         throw new UnsupportedOperationException("Direct access to underlying service object is not allowed in the SedaModel.  If this is for a unit test, make sure you are using the TestSedaModel ('seda-test')");
 470  
     }
 471  
 
 472  
     public QueueProfile getQueueProfile()
 473  
     {
 474  0
         return queueProfile;
 475  
     }
 476  
 
 477  
     public void setQueueProfile(QueueProfile queueProfile)
 478  
     {
 479  4
         this.queueProfile = queueProfile;
 480  4
     }
 481  
 
 482  
     public Integer getQueueTimeout()
 483  
     {
 484  60
         return queueTimeout;
 485  
     }
 486  
 
 487  
     public void setQueueTimeout(Integer queueTimeout)
 488  
     {
 489  390
         this.queueTimeout = queueTimeout;
 490  390
     }
 491  
 
 492  
     public ThreadingProfile getThreadingProfile()
 493  
     {
 494  0
         return threadingProfile;
 495  
     }
 496  
 
 497  
     public void setThreadingProfile(ThreadingProfile threadingProfile)
 498  
     {
 499  0
         this.threadingProfile = threadingProfile;
 500  0
     }
 501  
 
 502  
     public WorkManager getWorkManager()
 503  
     {
 504  0
         return workManager;
 505  
     }
 506  
 
 507  
     public void setWorkManager(WorkManager workManager)
 508  
     {
 509  0
         this.workManager = workManager;
 510  0
     }
 511  
 
 512  
     protected void dispatchToOutboundRouter(MuleEvent event, MuleMessage result) throws MessagingException
 513  
     {
 514  0
         super.dispatchToOutboundRouter(event, result);
 515  
         // TODO MULE-3077 SedaService should use a SEDA queue to dispatch to outbound
 516  
         // routers
 517  0
     }
 518  
 
 519  
     private class ComponentStageWorker implements Work
 520  
     {
 521  
         private MuleEvent event;
 522  
 
 523  
         public ComponentStageWorker(MuleEvent event)
 524  0
         {
 525  0
             this.event = event;
 526  0
         }
 527  
 
 528  
         public void run()
 529  
         {
 530  
             try
 531  
             {
 532  0
                 event = OptimizedRequestContext.criticalSetEvent(event);
 533  0
                 Object replyTo = event.getMessage().getReplyTo();
 534  0
                 ReplyToHandler replyToHandler = getReplyToHandler(event.getMessage(),
 535  
                     (InboundEndpoint) event.getEndpoint());
 536  0
                 MuleMessage result = component.onCall(event);
 537  0
                 dispatchToOutboundRouter(event, result);
 538  0
                 processReplyTo(event, result, replyToHandler, replyTo);
 539  
             }
 540  0
             catch (Exception e)
 541  
             {
 542  0
                 event.getSession().setValid(false);
 543  0
                 if (e instanceof MessagingException)
 544  
                 {
 545  0
                     handleException(e);
 546  
                 }
 547  
                 else
 548  
                 {
 549  0
                     handleException(new MessagingException(CoreMessages.eventProcessingFailedFor(getName()),
 550  
                         event.getMessage(), e));
 551  
                 }
 552  0
             }
 553  0
         }
 554  
 
 555  
         public void release()
 556  
         {
 557  
             // no-op
 558  0
         }
 559  
     }
 560  
 }