View Javadoc

1   /*
2    * $Id: JmsReplyToHandler.java 19640 2010-09-13 22:00:05Z tcarlson $
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.transformer.types.DataTypeFactory;
21  import org.mule.transport.DefaultReplyToHandler;
22  import org.mule.transport.jms.i18n.JmsMessages;
23  import org.mule.transport.jms.transformers.ObjectToJMSMessage;
24  import org.mule.util.StringMessageUtils;
25  import org.mule.util.StringUtils;
26  
27  import java.util.List;
28  
29  import javax.jms.Destination;
30  import javax.jms.JMSException;
31  import javax.jms.Message;
32  import javax.jms.MessageProducer;
33  import javax.jms.Queue;
34  import javax.jms.Session;
35  import javax.jms.Topic;
36  
37  /**
38   * <code>JmsReplyToHandler</code> will process a JMS replyTo or hand off to the
39   * default replyTo handler if the replyTo is a URL.
40   * The purpose of this class is to send a result on a ReplyTo destination if one
41   * has been set.
42   * Note that the {@link JmsMessageDispatcher} also contains logic for handling ReplyTo. However,
43   * the dispatcher is responsible attaching the replyTo information to the message and also
44   * receiving on the same replyTo if 'remoteSync' is set. The {@link JmsMessageDispatcher} never
45   * writes to the 'replyTo' destination.
46   */
47  public class JmsReplyToHandler extends DefaultReplyToHandler
48  {
49      private final JmsConnector connector;
50      private ObjectToJMSMessage toJmsMessage;
51  
52      public JmsReplyToHandler(JmsConnector connector, List<Transformer> transformers)
53      {
54          super(transformers, connector.getMuleContext());
55          this.connector = connector;
56          toJmsMessage = new ObjectToJMSMessage();
57      }
58  
59      @Override
60      public void processReplyTo(MuleEvent event, MuleMessage returnMessage, Object replyTo) throws MuleException
61      {
62          Destination replyToDestination = null;
63          MessageProducer replyToProducer = null;
64          Session session = null;
65          try
66          {
67              // now we need to send the response
68              if (replyTo instanceof Destination)
69              {
70                  replyToDestination = (Destination)replyTo;
71              }
72              if (replyToDestination == null)
73              {
74                  super.processReplyTo(event, returnMessage, replyTo);
75                  return;
76              }
77  
78              //This is a work around for JmsTransformers where the current endpoint needs
79              //to be set on the transformer so that a JMSMessage can be created correctly (the transformer needs a Session)
80              Class srcType = returnMessage.getPayload().getClass();
81              for (Transformer t : getTransformers())
82              {
83                  if (t.isSourceDataTypeSupported(DataTypeFactory.create(srcType)))
84                  {
85                      if (t.getEndpoint() == null)
86                      {
87                          t.setEndpoint(getEndpoint(event, "jms://temporary"));
88                          break;
89                      }
90                  }
91              }
92              returnMessage.applyTransformers(event, getTransformers());
93              Object payload = returnMessage.getPayload();
94  
95              if (replyToDestination instanceof Topic && replyToDestination instanceof Queue
96                      && connector.getJmsSupport() instanceof Jms102bSupport)
97              {
98                  logger.error(StringMessageUtils.getBoilerPlate("ReplyTo destination implements both Queue and Topic "
99                                                                 + "while complying with JMS 1.0.2b specification. "
100                                                                + "Please report your application server or JMS vendor name and version "
101                                                                + "to dev<_at_>mule.codehaus.org or http://mule.mulesoft.org/jira"));
102             }
103 
104             final boolean topic = connector.getTopicResolver().isTopic(replyToDestination);
105             session = connector.getSession(false, topic);
106 
107             //This mimics the OBjectToJmsMessage Transformer behaviour without needing an endpoint
108             //TODO clean this up, maybe make the transformer available via a utility class, passing in the Session
109             Message replyToMessage = JmsMessageUtils.toMessage(payload, session);
110             toJmsMessage.setJmsProperties(returnMessage, replyToMessage);
111 
112             processMessage(replyToMessage, event);
113             if (logger.isDebugEnabled())
114             {
115                 logger.debug("Sending jms reply to: " + replyToDestination + " ("
116                              + replyToDestination.getClass().getName() + ")");
117             }
118             replyToProducer = connector.getJmsSupport().createProducer(session, replyToDestination, topic);
119 
120             // QoS support
121             MuleMessage eventMsg = event.getMessage();
122             String ttlString = (String)eventMsg.getOutboundProperty(JmsConstants.TIME_TO_LIVE_PROPERTY);
123             String priorityString = (String)eventMsg.getOutboundProperty(JmsConstants.PRIORITY_PROPERTY);
124             String persistentDeliveryString = (String)eventMsg.getOutboundProperty(JmsConstants.PERSISTENT_DELIVERY_PROPERTY);
125 
126             String correlationIDString = replyToMessage.getJMSCorrelationID();
127             if (StringUtils.isBlank(correlationIDString))
128             {
129                 correlationIDString = eventMsg.getInboundProperty(JmsConstants.JMS_MESSAGE_ID);
130                 replyToMessage.setJMSCorrelationID(correlationIDString);
131             }
132 
133             if (event.getFlowConstruct() instanceof Service)
134             {
135                 ((Service) event.getFlowConstruct()).getStatistics().incSentReplyToEvent();
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 }