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