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