Coverage Report - org.mule.transport.jms.JmsMessageDispatcher
 
Classes in this File Line Coverage Branch Coverage Complexity
JmsMessageDispatcher
58%
73/126
43%
41/96
4.727
JmsMessageDispatcher$ReplyToListener
0%
0/13
N/A
4.727
 
 1  
 /*
 2  
  * $Id: JmsMessageDispatcher.java 11998 2008-06-10 16:51:33Z rossmason $
 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.transport.jms;
 12  
 
 13  
 import org.mule.DefaultMuleMessage;
 14  
 import org.mule.api.MuleEvent;
 15  
 import org.mule.api.MuleMessage;
 16  
 import org.mule.api.config.MuleProperties;
 17  
 import org.mule.api.endpoint.EndpointURI;
 18  
 import org.mule.api.endpoint.OutboundEndpoint;
 19  
 import org.mule.api.endpoint.EndpointBuilder;
 20  
 import org.mule.api.transport.Connector;
 21  
 import org.mule.api.transport.DispatchException;
 22  
 import org.mule.api.transport.MessageAdapter;
 23  
 import org.mule.transaction.IllegalTransactionStateException;
 24  
 import org.mule.transport.AbstractMessageDispatcher;
 25  
 import org.mule.transport.jms.i18n.JmsMessages;
 26  
 import org.mule.util.ClassUtils;
 27  
 import org.mule.util.NumberUtils;
 28  
 import org.mule.util.StringUtils;
 29  
 import org.mule.util.concurrent.Latch;
 30  
 import org.mule.util.concurrent.WaitableBoolean;
 31  
 
 32  
 import javax.jms.DeliveryMode;
 33  
 import javax.jms.Destination;
 34  
 import javax.jms.Message;
 35  
 import javax.jms.MessageConsumer;
 36  
 import javax.jms.MessageListener;
 37  
 import javax.jms.MessageProducer;
 38  
 import javax.jms.Session;
 39  
 import javax.jms.TemporaryQueue;
 40  
 import javax.jms.TemporaryTopic;
 41  
 
 42  
 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
 43  
 
 44  
 import org.apache.commons.lang.BooleanUtils;
 45  
 
 46  
 /**
 47  
  * <code>JmsMessageDispatcher</code> is responsible for dispatching messages to JMS
 48  
  * destinations. All JMS semantics apply and settings such as replyTo and QoS
 49  
  * properties are read from the event properties or defaults are used (according to
 50  
  * the JMS specification)
 51  
  */
 52  
 public class JmsMessageDispatcher extends AbstractMessageDispatcher
 53  
 {
 54  
 
 55  
     private JmsConnector connector;
 56  
     private Session cachedSession;
 57  
 
 58  
     public JmsMessageDispatcher(OutboundEndpoint endpoint)
 59  
     {
 60  119
         super(endpoint);
 61  119
         this.connector = (JmsConnector)endpoint.getConnector();
 62  119
     }
 63  
 
 64  
     protected void doDispatch(MuleEvent event) throws Exception
 65  
     {
 66  102
         dispatchMessage(event);
 67  102
     }
 68  
 
 69  
     protected void doConnect() throws Exception
 70  
     {
 71  
         // template method
 72  119
     }
 73  
 
 74  
     protected void doDisconnect() throws Exception
 75  
     {
 76  
         // template method
 77  119
     }
 78  
 
 79  
     private MuleMessage dispatchMessage(MuleEvent event) throws Exception
 80  
     {
 81  154
         Session session = null;
 82  154
         MessageProducer producer = null;
 83  154
         MessageConsumer consumer = null;
 84  154
         Destination replyTo = null;
 85  154
         boolean transacted = false;
 86  154
         boolean cached = false;
 87  154
         boolean remoteSync = useRemoteSync(event);
 88  
 
 89  153
         if (logger.isDebugEnabled())
 90  
         {
 91  0
             logger.debug("dispatching on endpoint: " + event.getEndpoint().getEndpointURI()
 92  
                          + ". MuleEvent id is: " + event.getId()
 93  
                          + ". Outbound transformers are: " + event.getEndpoint().getTransformers());
 94  
         }
 95  
 
 96  
         try
 97  
         {
 98  153
             session = connector.getSessionFromTransaction();
 99  154
             if (session != null)
 100  
             {
 101  28
                 transacted = true;
 102  
 
 103  
                 // If a transaction is running, we can not receive any messages
 104  
                 // in the same transaction.
 105  28
                 if (remoteSync)
 106  
                 {
 107  0
                     throw new IllegalTransactionStateException(
 108  
                         JmsMessages.connectorDoesNotSupportSyncReceiveWhenTransacted());
 109  
                 }
 110  
             }
 111  
             // Should we be caching sessions? Note this is not part of the JMS spec.
 112  
             // and is turned off by default.
 113  125
             else if (event.getMessage().getBooleanProperty(JmsConstants.CACHE_JMS_SESSIONS_PROPERTY,
 114  
                 connector.isCacheJmsSessions()))
 115  
             {
 116  0
                 cached = true;
 117  0
                 if (cachedSession != null)
 118  
                 {
 119  0
                     session = cachedSession;
 120  
                 }
 121  
                 else
 122  
                 {
 123  0
                     session = connector.getSession(event.getEndpoint());
 124  0
                     cachedSession = session;
 125  
                 }
 126  
             }
 127  
             else
 128  
             {
 129  126
                 session = connector.getSession(event.getEndpoint());
 130  126
                 if (event.getEndpoint().getTransactionConfig().isTransacted())
 131  
                 {
 132  18
                     transacted = true;
 133  
                 }
 134  
             }
 135  
 
 136  154
             EndpointURI endpointUri = event.getEndpoint().getEndpointURI();
 137  
 
 138  154
             boolean topic = connector.getTopicResolver().isTopic(event.getEndpoint(), true);
 139  
 
 140  154
             Destination dest = connector.getJmsSupport().createDestination(session, endpointUri.getAddress(),
 141  
                 topic);
 142  154
             producer = connector.getJmsSupport().createProducer(session, dest, topic);
 143  
 
 144  154
             Object message = event.transformMessage();
 145  154
             if (!(message instanceof Message))
 146  
             {
 147  0
                 throw new DispatchException(
 148  
                     JmsMessages.checkTransformer("JMS message", message.getClass(), connector.getName()),
 149  
                     event.getMessage(), event.getEndpoint());
 150  
             }
 151  
 
 152  152
             Message msg = (Message)message;
 153  153
             if (event.getMessage().getCorrelationId() != null)
 154  
             {
 155  128
                 msg.setJMSCorrelationID(event.getMessage().getCorrelationId());
 156  
             }
 157  
 
 158  152
             MuleMessage eventMsg = event.getMessage();
 159  
 
 160  
             // Some JMS implementations might not support the ReplyTo property.
 161  154
             if (connector.supportsProperty(JmsConstants.JMS_REPLY_TO))
 162  
             {
 163  153
                 Object tempReplyTo = eventMsg.removeProperty(JmsConstants.JMS_REPLY_TO);
 164  153
                 if(tempReplyTo==null)
 165  
                 {
 166  
                     //It may be a Mule URI or global endpoint Ref
 167  154
                     tempReplyTo = eventMsg.removeProperty(MuleProperties.MULE_REPLY_TO_PROPERTY);
 168  154
                     if(tempReplyTo!=null)
 169  
                     {
 170  0
                         if(tempReplyTo.toString().startsWith("jms://"))
 171  
                         {
 172  0
                             tempReplyTo = tempReplyTo.toString().substring(6);
 173  
                         }
 174  
                         else
 175  
                         {
 176  0
                             EndpointBuilder epb = event.getMuleContext().getRegistry().lookupEndpointBuilder(tempReplyTo.toString());
 177  0
                             if(epb != null)
 178  
                             {
 179  0
                                 tempReplyTo = epb.buildOutboundEndpoint().getEndpointURI().getAddress();
 180  
                             }
 181  
                         }
 182  
                     }
 183  
                 }
 184  153
                 if (tempReplyTo != null)
 185  
                 {
 186  0
                     if (tempReplyTo instanceof Destination)
 187  
                     {
 188  0
                         replyTo = (Destination)tempReplyTo;
 189  
                     }
 190  
                     else
 191  
                     {
 192  
                         // TODO AP should this drill-down be moved into the resolver as well?
 193  0
                         boolean replyToTopic = false;
 194  0
                         String reply = tempReplyTo.toString();
 195  0
                         int i = reply.indexOf(":");
 196  0
                         if (i > -1)
 197  
                         {
 198  
                             // TODO MULE-1409 this check will not work for ActiveMQ 4.x,
 199  
                             // as they have temp-queue://<destination> and temp-topic://<destination> URIs
 200  
                             // Extract to a custom resolver for ActiveMQ4.x
 201  
                             // The code path can be exercised, e.g. by a LoanBrokerESBTestCase
 202  0
                             String qtype = reply.substring(0, i);
 203  0
                             replyToTopic = JmsConstants.TOPIC_PROPERTY.equalsIgnoreCase(qtype);
 204  0
                             reply = reply.substring(i + 1);
 205  
                         }
 206  0
                         replyTo = connector.getJmsSupport().createDestination(session, reply, replyToTopic);
 207  
                     }
 208  
                 }
 209  
                 // Are we going to wait for a return event ?
 210  153
                 if (remoteSync && replyTo == null)
 211  
                 {
 212  2
                     replyTo = connector.getJmsSupport().createTemporaryDestination(session, topic);
 213  
                 }
 214  
                 // Set the replyTo property
 215  153
                 if (replyTo != null)
 216  
                 {
 217  2
                     msg.setJMSReplyTo(replyTo);
 218  
                 }
 219  
 
 220  
                 // Are we going to wait for a return event ?
 221  153
                 if (remoteSync)
 222  
                 {
 223  
                     try
 224  
                     {
 225  2
                         consumer = connector.getJmsSupport().createConsumer(session, replyTo, topic);
 226  
                     }
 227  0
                     catch (Exception e)
 228  
                     {
 229  0
                         logger.warn(e);
 230  2
                     }
 231  
                 }
 232  
             }
 233  
 
 234  
             // QoS support
 235  154
             String ttlString = (String)eventMsg.removeProperty(JmsConstants.TIME_TO_LIVE_PROPERTY);
 236  154
             String priorityString = (String)eventMsg.removeProperty(JmsConstants.PRIORITY_PROPERTY);
 237  154
             String persistentDeliveryString = (String)eventMsg.removeProperty(JmsConstants.PERSISTENT_DELIVERY_PROPERTY);
 238  
 
 239  153
             long ttl = StringUtils.isNotBlank(ttlString)
 240  
                                 ? NumberUtils.toLong(ttlString)
 241  
                                 : Message.DEFAULT_TIME_TO_LIVE;
 242  152
             int priority = StringUtils.isNotBlank(priorityString)
 243  
                                 ? NumberUtils.toInt(priorityString)
 244  
                                 : Message.DEFAULT_PRIORITY;
 245  154
             boolean persistent = StringUtils.isNotBlank(persistentDeliveryString)
 246  
                                 ? BooleanUtils.toBoolean(persistentDeliveryString)
 247  
                                 : connector.isPersistentDelivery();
 248  
 
 249  152
             if (connector.isHonorQosHeaders())
 250  
             {
 251  0
                 int priorityProp = eventMsg.getIntProperty(JmsConstants.JMS_PRIORITY, Connector.INT_VALUE_NOT_SET);
 252  0
                 int deliveryModeProp = eventMsg.getIntProperty(JmsConstants.JMS_DELIVERY_MODE, Connector.INT_VALUE_NOT_SET);
 253  
 
 254  0
                 if (priorityProp != Connector.INT_VALUE_NOT_SET)
 255  
                 {
 256  0
                     priority = priorityProp;
 257  
                 }
 258  0
                 if (deliveryModeProp != Connector.INT_VALUE_NOT_SET)
 259  
                 {
 260  0
                     persistent = deliveryModeProp == DeliveryMode.PERSISTENT;
 261  
                 }
 262  
             }
 263  
 
 264  154
             if (logger.isDebugEnabled())
 265  
             {
 266  0
                 logger.debug("Sending message of type " + ClassUtils.getSimpleName(msg.getClass()));
 267  
             }
 268  
 
 269  154
             if (consumer != null && topic)
 270  
             {
 271  
                 // need to register a listener for a topic
 272  0
                 Latch l = new Latch();
 273  0
                 ReplyToListener listener = new ReplyToListener(l);
 274  0
                 consumer.setMessageListener(listener);
 275  
 
 276  0
                 connector.getJmsSupport().send(producer, msg, persistent, priority, ttl, topic);
 277  
 
 278  0
                 int timeout = event.getTimeout();
 279  
 
 280  0
                 if (logger.isDebugEnabled())
 281  
                 {
 282  0
                     logger.debug("Waiting for return event for: " + timeout + " ms on " + replyTo);
 283  
                 }
 284  
 
 285  0
                 l.await(timeout, TimeUnit.MILLISECONDS);
 286  0
                 consumer.setMessageListener(null);
 287  0
                 listener.release();
 288  0
                 Message result = listener.getMessage();
 289  0
                 if (result == null)
 290  
                 {
 291  0
                     logger.debug("No message was returned via replyTo destination");
 292  0
                     return null;
 293  
                 }
 294  
                 else
 295  
                 {
 296  0
                     MessageAdapter adapter = connector.getMessageAdapter(result);
 297  0
                     return new DefaultMuleMessage(JmsMessageUtils.toObject(result, connector.getSpecification()),
 298  
                         adapter);
 299  
                 }
 300  
             }
 301  
             else
 302  
             {
 303  154
                 connector.getJmsSupport().send(producer, msg, persistent, priority, ttl, topic);
 304  154
                 if (consumer != null)
 305  
                 {
 306  2
                     int timeout = event.getTimeout();
 307  
 
 308  2
                     if (logger.isDebugEnabled())
 309  
                     {
 310  0
                         logger.debug("Waiting for return event for: " + timeout + " ms on " + replyTo);
 311  
                     }
 312  
 
 313  2
                     Message result = consumer.receive(timeout);
 314  2
                     if (result == null)
 315  
                     {
 316  0
                         logger.debug("No message was returned via replyTo destination");
 317  0
                         return null;
 318  
                     }
 319  
                     else
 320  
                     {
 321  2
                         MessageAdapter adapter = connector.getMessageAdapter(result);
 322  2
                         return new DefaultMuleMessage(
 323  
                             JmsMessageUtils.toObject(result, connector.getSpecification()), adapter);
 324  
                     }
 325  
                 }
 326  
             }
 327  152
             return null;
 328  
         }
 329  
         finally
 330  
         {
 331  0
             connector.closeQuietly(producer);
 332  154
             connector.closeQuietly(consumer);
 333  
 
 334  
             // TODO AP check if TopicResolver is to be utilized for temp destinations as well
 335  154
             if (replyTo != null && (replyTo instanceof TemporaryQueue || replyTo instanceof TemporaryTopic))
 336  
             {
 337  2
                 if (replyTo instanceof TemporaryQueue)
 338  
                 {
 339  2
                     connector.closeQuietly((TemporaryQueue)replyTo);
 340  
                 }
 341  
                 else
 342  
                 {
 343  
                     // hope there are no more non-standard tricks from JMS vendors
 344  
                     // here ;)
 345  0
                     connector.closeQuietly((TemporaryTopic)replyTo);
 346  
                 }
 347  
             }
 348  
 
 349  
             // If the session is from the current transaction, it is up to the
 350  
             // transaction to close it.
 351  154
             if (session != null && !cached && !transacted)
 352  
             {
 353  108
                 connector.closeQuietly(session);
 354  
             }
 355  
         }
 356  
     }
 357  
 
 358  
     protected MuleMessage doSend(MuleEvent event) throws Exception
 359  
     {
 360  52
         MuleMessage message = dispatchMessage(event);
 361  52
         return message;
 362  
     }
 363  
 
 364  
     protected void doDispose()
 365  
     {
 366  
         // template method
 367  119
     }
 368  
 
 369  
     private class ReplyToListener implements MessageListener
 370  
     {
 371  
         private final Latch latch;
 372  
         private volatile Message message;
 373  0
         private final WaitableBoolean released = new WaitableBoolean(false);
 374  
 
 375  
         public ReplyToListener(Latch latch)
 376  0
         {
 377  0
             this.latch = latch;
 378  0
         }
 379  
 
 380  
         public Message getMessage()
 381  
         {
 382  0
             return message;
 383  
         }
 384  
 
 385  
         public void release()
 386  
         {
 387  0
             released.set(true);
 388  0
         }
 389  
 
 390  
         public void onMessage(Message message)
 391  
         {
 392  0
             this.message = message;
 393  0
             latch.countDown();
 394  
             try
 395  
             {
 396  0
                 released.whenTrue(null);
 397  
             }
 398  0
             catch (InterruptedException e)
 399  
             {
 400  
                 // ignored
 401  0
             }
 402  0
         }
 403  
     }
 404  
 
 405  
 }