Coverage Report - org.mule.transport.jms.MultiConsumerJmsMessageReceiver
 
Classes in this File Line Coverage Branch Coverage Complexity
MultiConsumerJmsMessageReceiver
0%
0/43
0%
0/20
0
MultiConsumerJmsMessageReceiver$1
N/A
N/A
0
MultiConsumerJmsMessageReceiver$JmsWorker
0%
0/27
0%
0/16
0
MultiConsumerJmsMessageReceiver$SubReceiver
0%
0/69
0%
0/24
0
 
 1  
 /*
 2  
  * $Id: MultiConsumerJmsMessageReceiver.java 19835 2010-10-05 12:50:27Z dirk.olmes $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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.transport.jms;
 12  
 
 13  
 import org.mule.api.MuleException;
 14  
 import org.mule.api.MuleRuntimeException;
 15  
 import org.mule.api.construct.FlowConstruct;
 16  
 import org.mule.api.endpoint.InboundEndpoint;
 17  
 import org.mule.api.lifecycle.CreateException;
 18  
 import org.mule.api.lifecycle.LifecycleException;
 19  
 import org.mule.api.transaction.Transaction;
 20  
 import org.mule.api.transaction.TransactionException;
 21  
 import org.mule.api.transport.Connector;
 22  
 import org.mule.config.i18n.MessageFactory;
 23  
 import org.mule.transaction.TransactionCollection;
 24  
 import org.mule.transport.AbstractMessageReceiver;
 25  
 import org.mule.transport.AbstractReceiverWorker;
 26  
 import org.mule.transport.ConnectException;
 27  
 import org.mule.transport.jms.filters.JmsSelectorFilter;
 28  
 import org.mule.util.ClassUtils;
 29  
 
 30  
 import java.util.ArrayList;
 31  
 import java.util.Iterator;
 32  
 import java.util.List;
 33  
 
 34  
 import javax.jms.Destination;
 35  
 import javax.jms.JMSException;
 36  
 import javax.jms.Message;
 37  
 import javax.jms.MessageConsumer;
 38  
 import javax.jms.MessageListener;
 39  
 import javax.jms.Session;
 40  
 import javax.resource.spi.work.WorkException;
 41  
 
 42  
 import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
 43  
 
 44  
 import org.apache.commons.logging.Log;
 45  
 import org.apache.commons.logging.LogFactory;
 46  
 
 47  
 /**
 48  
  * In Mule an endpoint corresponds to a single receiver. It's up to the receiver to do multithreaded consumption and
 49  
  * resource allocation, if needed. This class honors the <code>numberOfConcurrentTransactedReceivers</code> strictly
 50  
  * and will create exactly this number of consumers.
 51  
  */
 52  0
 public class MultiConsumerJmsMessageReceiver extends AbstractMessageReceiver
 53  
 {
 54  
     protected final List<SubReceiver> consumers;
 55  
 
 56  
     protected final int receiversCount;
 57  
 
 58  
     private final JmsConnector jmsConnector;
 59  
 
 60  
     public MultiConsumerJmsMessageReceiver(Connector connector, FlowConstruct flowConstruct, InboundEndpoint endpoint)
 61  
             throws CreateException
 62  
     {
 63  0
         super(connector, flowConstruct, endpoint);
 64  
 
 65  0
         jmsConnector = (JmsConnector) connector;
 66  
 
 67  0
         final boolean isTopic = jmsConnector.getTopicResolver().isTopic(endpoint, true);
 68  0
         if (isTopic && jmsConnector.getNumberOfConsumers() != 1)
 69  
         {
 70  0
             if (logger.isInfoEnabled())
 71  
             {
 72  0
                 logger.info("Destination " + getEndpoint().getEndpointURI() + " is a topic, but " + jmsConnector.getNumberOfConsumers() +
 73  
                                 " receivers have been requested. Will configure only 1.");
 74  
             }
 75  0
             receiversCount = 1;
 76  
         }
 77  
         else
 78  
         {
 79  0
             receiversCount = jmsConnector.getNumberOfConsumers();
 80  
         }
 81  0
         if (logger.isDebugEnabled())
 82  
         {
 83  0
             logger.debug("Creating " + receiversCount + " sub-receivers for " + endpoint.getEndpointURI());
 84  
         }
 85  
 
 86  0
         consumers = new CopyOnWriteArrayList();
 87  0
     }
 88  
         
 89  
     @Override
 90  
     protected void doStart() throws MuleException
 91  
     {
 92  0
         logger.debug("doStart()");
 93  
         SubReceiver sub;
 94  0
         for (Iterator<SubReceiver> it = consumers.iterator(); it.hasNext();)
 95  
         {
 96  0
             sub = it.next();
 97  0
             sub.doStart();
 98  
         }
 99  0
     }
 100  
 
 101  
     @Override
 102  
     protected void doStop() throws MuleException
 103  
     {
 104  0
         logger.debug("doStop()");
 105  0
         if (consumers != null)
 106  
         {
 107  
             SubReceiver sub;
 108  0
             for (Iterator<SubReceiver> it = consumers.iterator(); it.hasNext();)
 109  
             {
 110  0
                 sub = it.next();
 111  0
                 sub.doStop(true);
 112  
             }
 113  
         }
 114  0
     }
 115  
 
 116  
     @Override
 117  
     protected void doConnect() throws Exception
 118  
     {
 119  0
         logger.debug("doConnect()");
 120  
 
 121  0
         if (!consumers.isEmpty())
 122  
         {
 123  0
             throw new IllegalStateException("List should be empty, there may be a concurrency issue here (see EE-1275)");
 124  
         }
 125  
         
 126  
         SubReceiver sub;
 127  0
         for (int i = 0; i < receiversCount; i++)
 128  
         {
 129  0
             sub = new SubReceiver();
 130  0
             sub.doConnect();
 131  0
             consumers.add(sub);
 132  
         }
 133  0
     }
 134  
 
 135  
     @Override
 136  
     protected void doDisconnect() throws Exception
 137  
     {
 138  0
         logger.debug("doDisconnect()");
 139  
 
 140  
         SubReceiver sub;
 141  0
         for (Iterator<SubReceiver> it = consumers.iterator(); it.hasNext();)
 142  
         {
 143  0
             sub = it.next();
 144  
             try
 145  
             {
 146  0
                 sub.doDisconnect();
 147  
             }
 148  
             finally
 149  
             {
 150  0
                 sub = null;
 151  0
             }
 152  
         }
 153  0
         consumers.clear();
 154  0
     }
 155  
 
 156  
     @Override
 157  
     protected void doDispose()
 158  
     {
 159  0
         logger.debug("doDispose()");
 160  0
     }
 161  
 
 162  
     @Override
 163  
     protected boolean isDoStartMustFollowDoConnect()
 164  
     {
 165  0
         return true;
 166  
     }
 167  
 
 168  0
     private class SubReceiver implements MessageListener
 169  
     {
 170  0
         private final Log subLogger = LogFactory.getLog(getClass());
 171  
 
 172  
         private volatile Session session;
 173  
         private volatile MessageConsumer consumer;
 174  
 
 175  
         protected volatile boolean connected;
 176  
         protected volatile boolean started;
 177  
         
 178  
         protected void doConnect() throws MuleException
 179  
         {
 180  0
             subLogger.debug("SUB doConnect()");
 181  
             try
 182  
             {
 183  0
                 createConsumer();
 184  
             }
 185  0
             catch (Exception e)
 186  
             {
 187  0
                 throw new LifecycleException(e, this);
 188  0
             }
 189  0
             connected = true;
 190  0
         }
 191  
 
 192  
         protected void doDisconnect() throws MuleException
 193  
         {
 194  0
             subLogger.debug("SUB doDisconnect()");
 195  0
             if (started)
 196  
             {
 197  0
                 doStop(true);
 198  
             }
 199  0
             closeConsumer();
 200  0
             connected = false;
 201  0
         }
 202  
 
 203  
         protected void closeConsumer()
 204  
         {
 205  0
             jmsConnector.closeQuietly(consumer);
 206  0
             consumer = null;
 207  0
             jmsConnector.closeQuietly(session);
 208  0
             session = null;
 209  0
         }
 210  
 
 211  
         protected void doStart() throws MuleException
 212  
         {
 213  0
             subLogger.debug("SUB doStart()");
 214  0
             if (!connected)
 215  
             {
 216  0
                 doConnect();
 217  
             }
 218  
             
 219  
             try
 220  
             { 
 221  0
                 consumer.setMessageListener(this);
 222  0
                 started = true;
 223  
             }
 224  0
             catch (JMSException e)
 225  
             {
 226  0
                 throw new LifecycleException(e, this);
 227  0
             }
 228  0
         }
 229  
 
 230  
         /**
 231  
          * Stop the subreceiver.
 232  
          * @param force - if true, any exceptions will be logged but the subreceiver will be considered stopped regardless
 233  
          * @throws MuleException only if force = false
 234  
          */
 235  
         protected void doStop(boolean force) throws MuleException
 236  
         {
 237  0
             subLogger.debug("SUB doStop()");
 238  
 
 239  0
             if (consumer != null)
 240  
             {
 241  
                 try
 242  
                 {
 243  0
                     consumer.setMessageListener(null);
 244  0
                     started = false;
 245  
                 }
 246  0
                 catch (JMSException e)
 247  
                 {
 248  0
                     if (force)
 249  
                     {
 250  0
                         logger.warn("Unable to cleanly stop subreceiver: " + e.getMessage());
 251  0
                         started = false;
 252  
                     }
 253  
                     else
 254  
                     {
 255  0
                         throw new LifecycleException(e, this);
 256  
                     }
 257  0
                 }
 258  
             }
 259  0
         }
 260  
 
 261  
         /**
 262  
          * Create a consumer for the jms destination.
 263  
          */
 264  
         protected void createConsumer() throws Exception
 265  
         {
 266  0
             subLogger.debug("SUB createConsumer()");
 267  
             
 268  
             try
 269  
             {
 270  0
                 JmsSupport jmsSupport = jmsConnector.getJmsSupport();
 271  0
                 boolean topic = jmsConnector.getTopicResolver().isTopic(endpoint, true);
 272  
 
 273  
                 // Create session if none exists
 274  0
                 if (session == null)
 275  
                 {
 276  0
                     session = jmsConnector.getSession(endpoint);
 277  
                 }
 278  
 
 279  
                 // Create destination
 280  0
                 Destination dest = jmsSupport.createDestination(session, endpoint);
 281  
 
 282  
                 // Extract jms selector
 283  0
                 String selector = null;
 284  0
                 if (endpoint.getFilter() != null && endpoint.getFilter() instanceof JmsSelectorFilter)
 285  
                 {
 286  0
                     selector = ((JmsSelectorFilter) endpoint.getFilter()).getExpression();
 287  
                 }
 288  
                 else
 289  
                 {
 290  0
                     if (endpoint.getProperties() != null)
 291  
                     {
 292  
                         // still allow the selector to be set as a property on the endpoint
 293  
                         // to be backward compatable
 294  0
                         selector = (String) endpoint.getProperties().get(JmsConstants.JMS_SELECTOR_PROPERTY);
 295  
                     }
 296  
                 }
 297  0
                 String tempDurable = (String) endpoint.getProperties().get(JmsConstants.DURABLE_PROPERTY);
 298  0
                 boolean durable = jmsConnector.isDurable();
 299  0
                 if (tempDurable != null)
 300  
                 {
 301  0
                     durable = Boolean.valueOf(tempDurable);
 302  
                 }
 303  
 
 304  
                 // Get the durable subscriber name if there is one
 305  0
                 String durableName = (String) endpoint.getProperties().get(JmsConstants.DURABLE_NAME_PROPERTY);
 306  0
                 if (durableName == null && durable && topic)
 307  
                 {
 308  0
                     durableName = "mule." + jmsConnector.getName() + "." + endpoint.getEndpointURI().getAddress();
 309  0
                     logger.debug("Jms Connector for this receiver is durable but no durable name has been specified. Defaulting to: "
 310  
                                  + durableName);
 311  
                 }
 312  
 
 313  
                 // Create consumer
 314  0
                 consumer = jmsSupport.createConsumer(session, dest, selector, jmsConnector.isNoLocal(), durableName,
 315  
                                                      topic, endpoint);
 316  
             }
 317  0
             catch (JMSException e)
 318  
             {
 319  0
                 throw new ConnectException(e, MultiConsumerJmsMessageReceiver.this);
 320  0
             }
 321  0
         }
 322  
 
 323  
         public void onMessage(final Message message)
 324  
         {
 325  
             try
 326  
             {
 327  
                 // This must be the doWork() to preserve the transactional context.
 328  
                 // We are already running in the consumer thread by this time.
 329  
                 // The JmsWorker class is a one-off executor which is abandoned after it's done 
 330  
                 // and is easily garbage-collected (confirmed with a profiler)
 331  0
                 getWorkManager().doWork(new JmsWorker(message, MultiConsumerJmsMessageReceiver.this, this));
 332  
             }
 333  0
             catch (WorkException e)
 334  
             {
 335  0
                 throw new MuleRuntimeException(MessageFactory.createStaticMessage(
 336  
                         "Couldn't submit a work item to the WorkManager"), e);
 337  0
             }
 338  0
         }
 339  
     }
 340  
 
 341  
     protected class JmsWorker extends AbstractReceiverWorker
 342  
     {
 343  
         private final SubReceiver subReceiver;
 344  
 
 345  
         public JmsWorker(Message message, AbstractMessageReceiver receiver, SubReceiver subReceiver)
 346  0
         {
 347  0
             super(new ArrayList<Object>(1), receiver);
 348  0
             this.subReceiver = subReceiver;
 349  0
             messages.add(message);
 350  0
         }
 351  
 
 352  
         @Override
 353  
         protected Object preProcessMessage(Object message) throws Exception
 354  
         {
 355  0
             Message m = (Message) message;
 356  
 
 357  0
             if (logger.isDebugEnabled())
 358  
             {
 359  0
                 logger.debug("Message received it is of type: " +
 360  
                              ClassUtils.getSimpleName(message.getClass()));
 361  0
                 if (m.getJMSDestination() != null)
 362  
                 {
 363  0
                     logger.debug("Message received on " + m.getJMSDestination() + " ("
 364  
                                  + m.getJMSDestination().getClass().getName() + ")");
 365  
                 }
 366  
                 else
 367  
                 {
 368  0
                     logger.debug("Message received on unknown destination");
 369  
                 }
 370  0
                 logger.debug("Message CorrelationId is: " + m.getJMSCorrelationID());
 371  0
                 logger.debug("Jms Message Id is: " + m.getJMSMessageID());
 372  
             }
 373  
 
 374  0
             if (m.getJMSRedelivered())
 375  
             {
 376  
                 // lazily create the redelivery handler
 377  0
                 RedeliveryHandler redeliveryHandler = jmsConnector.getRedeliveryHandlerFactory().create();
 378  0
                 redeliveryHandler.setConnector(jmsConnector);
 379  0
                 if (logger.isDebugEnabled())
 380  
                 {
 381  0
                     logger.debug("Message with correlationId: " + m.getJMSCorrelationID()
 382  
                                  + " has redelivered flag set, handing off to Redelivery Handler");
 383  
                 }
 384  0
                 redeliveryHandler.handleRedelivery(m);
 385  
             }
 386  0
             return m;
 387  
 
 388  
         }
 389  
 
 390  
         @Override
 391  
         protected void bindTransaction(Transaction tx) throws TransactionException
 392  
         {
 393  0
             if (tx instanceof JmsTransaction || tx instanceof TransactionCollection)
 394  
             {
 395  0
                 if (logger.isDebugEnabled())
 396  
                 {
 397  0
                     logger.debug("Binding " + subReceiver.session + " to " + jmsConnector.getConnection());
 398  
                 }
 399  0
                 tx.bindResource(jmsConnector.getConnection(), subReceiver.session);
 400  
             }
 401  
             else
 402  
             {
 403  0
                 if (tx instanceof JmsClientAcknowledgeTransaction)
 404  
                 {
 405  
                     //We should still bind the session to the transaction, but we also need the message itself
 406  
                     //since that is the object that gets Acknowledged
 407  
                     //tx.bindResource(jmsConnector.getConnection(), session);
 408  0
                     ((JmsClientAcknowledgeTransaction) tx).setMessage((Message) messages.get(0));
 409  
                 }
 410  
             }
 411  0
         }
 412  
     }
 413  
 
 414  
 }