View Javadoc

1   /*
2    * $Id: JmsReplyToHandler.java 22813 2011-09-01 19:27:14Z pablo.lagreca $
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.MuleContext;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleException;
16  import org.mule.api.MuleMessage;
17  import org.mule.api.endpoint.EndpointBuilder;
18  import org.mule.api.endpoint.OutboundEndpoint;
19  import org.mule.api.service.Service;
20  import org.mule.api.transformer.Transformer;
21  import org.mule.api.transport.DispatchException;
22  import org.mule.endpoint.EndpointURIEndpointBuilder;
23  import org.mule.management.stats.ServiceStatistics;
24  import org.mule.transport.DefaultReplyToHandler;
25  import org.mule.transport.jms.i18n.JmsMessages;
26  import org.mule.transport.jms.transformers.ObjectToJMSMessage;
27  import org.mule.util.StringMessageUtils;
28  import org.mule.util.StringUtils;
29  
30  import java.util.List;
31  
32  import javax.jms.Destination;
33  import javax.jms.JMSException;
34  import javax.jms.Message;
35  import javax.jms.MessageProducer;
36  import javax.jms.Queue;
37  import javax.jms.Session;
38  import javax.jms.Topic;
39  
40  /**
41   * <code>JmsReplyToHandler</code> will process a JMS replyTo or hand off to the
42   * default replyTo handler if the replyTo is a URL.
43   * The purpose of this class is to send a result on a ReplyTo destination if one
44   * has been set.
45   * Note that the {@link JmsMessageDispatcher} also contains logic for handling ReplyTo. However,
46   * the dispatcher is responsible attaching the replyTo information to the message and also
47   * receiving on the same replyTo if 'remoteSync' is set. The {@link JmsMessageDispatcher} never
48   * writes to the 'replyTo' destination.
49   */
50  public class JmsReplyToHandler extends DefaultReplyToHandler
51  {
52      /**
53       * Serial version
54       */
55      private static final long serialVersionUID = 1L;
56  
57      private transient JmsConnector jmsConnector;
58      private transient ObjectToJMSMessage toJmsMessage;
59  
60      public JmsReplyToHandler(JmsConnector connector)
61      {
62          super(connector.getMuleContext());
63          this.connector = this.jmsConnector = connector;
64          toJmsMessage = new ObjectToJMSMessage();
65      }
66  
67      @Override
68      public void processReplyTo(MuleEvent event, MuleMessage returnMessage, Object replyTo) throws MuleException
69      {
70          Destination replyToDestination = null;  
71          MessageProducer replyToProducer = null;
72          Session session = null;
73          try
74          {
75              // now we need to send the response
76              if (replyTo instanceof Destination)
77              {
78                  replyToDestination = (Destination)replyTo;
79              }
80              if (replyToDestination == null)
81              {
82                  super.processReplyTo(event, returnMessage, replyTo);
83                  return;
84              }
85  
86              Class srcType = returnMessage.getPayload().getClass();
87              EndpointBuilder endpointBuilder = new EndpointURIEndpointBuilder(String.format("%s://temporary",connector.getProtocol()), muleContext);
88              endpointBuilder.setConnector(jmsConnector);
89              OutboundEndpoint tempEndpoint = muleContext.getEndpointFactory().getOutboundEndpoint(endpointBuilder);
90              
91              List<Transformer> defaultTransportTransformers =  ((org.mule.transport.AbstractConnector) jmsConnector).getDefaultOutboundTransformers(tempEndpoint);
92              
93              returnMessage.applyTransformers(event, defaultTransportTransformers);
94              
95              Object payload = returnMessage.getPayload();
96  
97              if (replyToDestination instanceof Topic && replyToDestination instanceof Queue
98                      && jmsConnector.getJmsSupport() instanceof Jms102bSupport)
99              {
100                 logger.error(StringMessageUtils.getBoilerPlate("ReplyTo destination implements both Queue and Topic "
101                                                                + "while complying with JMS 1.0.2b specification. "
102                                                                + "Please report your application server or JMS vendor name and version "
103                                                                + "to dev<_at_>mule.codehaus.org or http://mule.mulesoft.org/jira"));
104             }
105 
106             final boolean topic = jmsConnector.getTopicResolver().isTopic(replyToDestination);
107             session = jmsConnector.getSession(false, topic);
108 
109             //This mimics the OBjectToJmsMessage Transformer behaviour without needing an endpoint
110             //TODO clean this up, maybe make the transformer available via a utility class, passing in the Session
111             Message replyToMessage = JmsMessageUtils.toMessage(payload, session);
112             toJmsMessage.setJmsProperties(returnMessage, replyToMessage);
113 
114             processMessage(replyToMessage, event);
115             if (logger.isDebugEnabled())
116             {
117                 logger.debug("Sending jms reply to: " + replyToDestination + " ("
118                              + replyToDestination.getClass().getName() + ")");
119             }
120             replyToProducer = jmsConnector.getJmsSupport().createProducer(session, replyToDestination, topic);
121 
122             // QoS support
123             MuleMessage eventMsg = event.getMessage();
124             String ttlString = (String)eventMsg.getOutboundProperty(JmsConstants.TIME_TO_LIVE_PROPERTY);
125             String priorityString = (String)eventMsg.getOutboundProperty(JmsConstants.PRIORITY_PROPERTY);
126             String persistentDeliveryString = (String)eventMsg.getOutboundProperty(JmsConstants.PERSISTENT_DELIVERY_PROPERTY);
127 
128             String correlationIDString = replyToMessage.getJMSCorrelationID();
129             if (StringUtils.isBlank(correlationIDString))
130             {
131                 correlationIDString = eventMsg.getInboundProperty(JmsConstants.JMS_MESSAGE_ID);
132                 replyToMessage.setJMSCorrelationID(correlationIDString);
133             }
134 
135             if (event.getFlowConstruct() instanceof Service)
136             {
137                 ServiceStatistics stats = ((Service) event.getFlowConstruct()).getStatistics();
138                 if (stats.isEnabled())
139                 {
140                     stats.incSentReplyToEvent();
141                 }
142             }
143 
144             if (ttlString == null && priorityString == null && persistentDeliveryString == null)
145             {
146                 jmsConnector.getJmsSupport().send(replyToProducer, replyToMessage, topic, null);
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                                 : jmsConnector.isPersistentDelivery();
164 
165                 jmsConnector.getJmsSupport().send(replyToProducer, replyToMessage, persistent, priority, ttl,
166                     topic, null);
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             jmsConnector.closeQuietly(replyToProducer);
182             jmsConnector.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 
208     @Override
209     public void initAfterDeserialisation(MuleContext muleContext) throws MuleException
210     {
211         super.initAfterDeserialisation(muleContext);
212         this.toJmsMessage = new ObjectToJMSMessage();
213         this.jmsConnector = (JmsConnector) this.connector;
214     }
215 }