View Javadoc

1   /*
2    * $Id: JmsReplyToHandler.java 20343 2010-11-24 21:02:10Z mike.schilling $
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.MuleEvent;
14  import org.mule.api.MuleException;
15  import org.mule.api.MuleMessage;
16  import org.mule.api.endpoint.ImmutableEndpoint;
17  import org.mule.api.service.Service;
18  import org.mule.api.transformer.Transformer;
19  import org.mule.api.transport.DispatchException;
20  import org.mule.management.stats.ServiceStatistics;
21  import org.mule.transformer.types.DataTypeFactory;
22  import org.mule.transport.DefaultReplyToHandler;
23  import org.mule.transport.jms.i18n.JmsMessages;
24  import org.mule.transport.jms.transformers.ObjectToJMSMessage;
25  import org.mule.util.StringMessageUtils;
26  import org.mule.util.StringUtils;
27  
28  import java.util.List;
29  
30  import javax.jms.Destination;
31  import javax.jms.JMSException;
32  import javax.jms.Message;
33  import javax.jms.MessageProducer;
34  import javax.jms.Queue;
35  import javax.jms.Session;
36  import javax.jms.Topic;
37  
38  /**
39   * <code>JmsReplyToHandler</code> will process a JMS replyTo or hand off to the
40   * default replyTo handler if the replyTo is a URL.
41   * The purpose of this class is to send a result on a ReplyTo destination if one
42   * has been set.
43   * Note that the {@link JmsMessageDispatcher} also contains logic for handling ReplyTo. However,
44   * the dispatcher is responsible attaching the replyTo information to the message and also
45   * receiving on the same replyTo if 'remoteSync' is set. The {@link JmsMessageDispatcher} never
46   * writes to the 'replyTo' destination.
47   */
48  public class JmsReplyToHandler extends DefaultReplyToHandler
49  {
50      private final JmsConnector connector;
51      private ObjectToJMSMessage toJmsMessage;
52  
53      public JmsReplyToHandler(JmsConnector connector, List<Transformer> transformers)
54      {
55          super(transformers, connector.getMuleContext());
56          this.connector = connector;
57          toJmsMessage = new ObjectToJMSMessage();
58      }
59  
60      @Override
61      public void processReplyTo(MuleEvent event, MuleMessage returnMessage, Object replyTo) throws MuleException
62      {
63          Destination replyToDestination = null;
64          MessageProducer replyToProducer = null;
65          Session session = null;
66          try
67          {
68              // now we need to send the response
69              if (replyTo instanceof Destination)
70              {
71                  replyToDestination = (Destination)replyTo;
72              }
73              if (replyToDestination == null)
74              {
75                  super.processReplyTo(event, returnMessage, replyTo);
76                  return;
77              }
78  
79              //This is a work around for JmsTransformers where the current endpoint needs
80              //to be set on the transformer so that a JMSMessage can be created correctly (the transformer needs a Session)
81              Class srcType = returnMessage.getPayload().getClass();
82              for (Transformer t : getTransformers())
83              {
84                  if (t.isSourceDataTypeSupported(DataTypeFactory.create(srcType)))
85                  {
86                      if (t.getEndpoint() == null)
87                      {
88                          t.setEndpoint(getEndpoint(event, "jms://temporary"));
89                          break;
90                      }
91                  }
92              }
93              returnMessage.applyTransformers(event, getTransformers());
94              Object payload = returnMessage.getPayload();
95  
96              if (replyToDestination instanceof Topic && replyToDestination instanceof Queue
97                      && connector.getJmsSupport() instanceof Jms102bSupport)
98              {
99                  logger.error(StringMessageUtils.getBoilerPlate("ReplyTo destination implements both Queue and Topic "
100                                                                + "while complying with JMS 1.0.2b specification. "
101                                                                + "Please report your application server or JMS vendor name and version "
102                                                                + "to dev<_at_>mule.codehaus.org or http://mule.mulesoft.org/jira"));
103             }
104 
105             final boolean topic = connector.getTopicResolver().isTopic(replyToDestination);
106             session = connector.getSession(false, topic);
107 
108             //This mimics the OBjectToJmsMessage Transformer behaviour without needing an endpoint
109             //TODO clean this up, maybe make the transformer available via a utility class, passing in the Session
110             Message replyToMessage = JmsMessageUtils.toMessage(payload, session);
111             toJmsMessage.setJmsProperties(returnMessage, replyToMessage);
112 
113             processMessage(replyToMessage, event);
114             if (logger.isDebugEnabled())
115             {
116                 logger.debug("Sending jms reply to: " + replyToDestination + " ("
117                              + replyToDestination.getClass().getName() + ")");
118             }
119             replyToProducer = connector.getJmsSupport().createProducer(session, replyToDestination, topic);
120 
121             // QoS support
122             MuleMessage eventMsg = event.getMessage();
123             String ttlString = (String)eventMsg.getOutboundProperty(JmsConstants.TIME_TO_LIVE_PROPERTY);
124             String priorityString = (String)eventMsg.getOutboundProperty(JmsConstants.PRIORITY_PROPERTY);
125             String persistentDeliveryString = (String)eventMsg.getOutboundProperty(JmsConstants.PERSISTENT_DELIVERY_PROPERTY);
126 
127             String correlationIDString = replyToMessage.getJMSCorrelationID();
128             if (StringUtils.isBlank(correlationIDString))
129             {
130                 correlationIDString = eventMsg.getInboundProperty(JmsConstants.JMS_MESSAGE_ID);
131                 replyToMessage.setJMSCorrelationID(correlationIDString);
132             }
133 
134             if (event.getFlowConstruct() instanceof Service)
135             {
136                 ServiceStatistics stats = ((Service) event.getFlowConstruct()).getStatistics();
137                 if (stats.isEnabled())
138                 {
139                     stats.incSentReplyToEvent();
140                 }
141             }
142 
143             final ImmutableEndpoint endpoint = event.getEndpoint();
144             if (ttlString == null && priorityString == null && persistentDeliveryString == null)
145             {
146                 connector.getJmsSupport().send(replyToProducer, replyToMessage, topic, endpoint);
147             }
148             else
149             {
150                 long ttl = Message.DEFAULT_TIME_TO_LIVE;
151                 int priority = Message.DEFAULT_PRIORITY;
152 
153                 if (ttlString != null)
154                 {
155                     ttl = Long.parseLong(ttlString);
156                 }
157                 if (priorityString != null)
158                 {
159                     priority = Integer.parseInt(priorityString);
160                 }
161                 boolean persistent = StringUtils.isNotBlank(persistentDeliveryString)
162                                 ? Boolean.valueOf(persistentDeliveryString)
163                                 : connector.isPersistentDelivery();
164 
165                 connector.getJmsSupport().send(replyToProducer, replyToMessage, persistent, priority, ttl,
166                     topic, endpoint);
167             }
168 
169             if (logger.isInfoEnabled())
170             {
171                 logger.info(String.format("Reply Message sent to: %s with correlationID:%s", replyToDestination, correlationIDString));
172             }
173         }
174         catch (Exception e)
175         {
176             throw new DispatchException(
177                 JmsMessages.failedToCreateAndDispatchResponse(replyToDestination), event, null, e);
178         }
179         finally
180         {
181             connector.closeQuietly(replyToProducer);
182             connector.closeSessionIfNoTransactionActive(session);
183         }
184     }
185 
186     protected void processMessage(Message replyToMessage, MuleEvent event) throws JMSException
187     {
188         replyToMessage.setJMSReplyTo(null);
189 
190         // If JMS correlation ID exists in the incoming message - use it for the outbound message;
191         // otherwise use JMS Message ID
192         MuleMessage eventMsg = event.getMessage();
193         String jmsCorrelationId = eventMsg.getInboundProperty("JMSCorrelationID");
194         if (jmsCorrelationId == null)
195         {
196             jmsCorrelationId = eventMsg.getInboundProperty("JMSMessageID");
197         }
198         if (jmsCorrelationId != null)
199         {
200             replyToMessage.setJMSCorrelationID(jmsCorrelationId);
201         }
202         if (logger.isDebugEnabled())
203         {
204             logger.debug("replyTo message is " + replyToMessage);
205         }
206     }
207 }