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