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.MuleException;
10  import org.mule.api.MuleMessage;
11  import org.mule.api.construct.FlowConstruct;
12  import org.mule.api.endpoint.InboundEndpoint;
13  import org.mule.api.lifecycle.CreateException;
14  import org.mule.api.lifecycle.StartException;
15  import org.mule.api.lifecycle.StopException;
16  import org.mule.api.transaction.Transaction;
17  import org.mule.api.transaction.TransactionCallback;
18  import org.mule.api.transport.Connector;
19  import org.mule.api.transport.MessageReceiver;
20  import org.mule.transaction.TransactionCoordination;
21  import org.mule.transaction.TransactionTemplate;
22  import org.mule.transport.AbstractMessageReceiver;
23  import org.mule.transport.ConnectException;
24  import org.mule.transport.jms.filters.JmsSelectorFilter;
25  import org.mule.transport.jms.redelivery.RedeliveryHandler;
26  import org.mule.util.ClassUtils;
27  
28  import javax.jms.Destination;
29  import javax.jms.JMSException;
30  import javax.jms.Message;
31  import javax.jms.MessageConsumer;
32  import javax.jms.MessageListener;
33  import javax.jms.Session;
34  import javax.jms.Topic;
35  import javax.resource.spi.work.Work;
36  
37  public class TransactedSingleResourceJmsMessageReceiver extends AbstractMessageReceiver
38          implements MessageListener
39  {
40      protected JmsConnector connector;
41      protected RedeliveryHandler redeliveryHandler;
42      protected MessageConsumer consumer;
43      protected Session session;
44      protected boolean startOnConnect = false;
45  
46      /** determines whether messages will be received in a transaction template */
47      protected boolean receiveMessagesInTransaction = true;
48  
49      /** determines whether Multiple receivers are created to improve throughput */
50      protected boolean useMultipleReceivers = true;
51  
52      
53      public TransactedSingleResourceJmsMessageReceiver(Connector connector,
54                                                        FlowConstruct flowConstruct,
55                                                        InboundEndpoint endpoint) throws CreateException
56      {
57  
58          super(connector, flowConstruct, endpoint);
59  
60          this.connector = (JmsConnector) connector;
61  
62          // TODO check which properties being set in the TransecteJmsMessage receiver
63          // are needed...
64  
65          try
66          {
67              redeliveryHandler = this.connector.getRedeliveryHandlerFactory().create();
68              redeliveryHandler.setConnector(this.connector);
69          }
70          catch (Exception e)
71          {
72              throw new CreateException(e, this);
73          }
74      }
75  
76      @Override
77      protected void doDispose()
78      {
79          // template method
80      }
81  
82      @Override
83      protected void doConnect() throws Exception
84      {
85          try
86          {
87              JmsSupport jmsSupport = this.connector.getJmsSupport();
88              // Create session if none exists
89              if (session == null)
90              {
91                  session = this.connector.getSession(endpoint);
92              }
93  
94              // Create destination
95              boolean topic = connector.getTopicResolver().isTopic(endpoint);
96  
97              Destination dest = jmsSupport.createDestination(session, endpoint);
98              // Extract jms selector
99              String selector = null;
100             if (endpoint.getFilter() != null && endpoint.getFilter() instanceof JmsSelectorFilter)
101             {
102                 selector = ((JmsSelectorFilter) endpoint.getFilter()).getExpression();
103             }
104             else if (endpoint.getProperties() != null)
105             {
106                 // still allow the selector to be set as a property on the endpoint
107                 // to be backward compatable
108                 selector = (String) endpoint.getProperties().get(JmsConstants.JMS_SELECTOR_PROPERTY);
109             }
110             String tempDurable = (String) endpoint.getProperties().get(JmsConstants.DURABLE_PROPERTY);
111             boolean durable = connector.isDurable();
112             if (tempDurable != null)
113             {
114                 durable = Boolean.valueOf(tempDurable).booleanValue();
115             }
116 
117             // Get the durable subscriber name if there is one
118             String durableName = (String) endpoint.getProperties().get(JmsConstants.DURABLE_NAME_PROPERTY);
119             if (durableName == null && durable && dest instanceof Topic)
120             {
121                 durableName = "mule." + connector.getName() + "." + endpoint.getEndpointURI().getAddress();
122                 logger.debug("Jms Connector for this receiver is durable but no durable name has been specified. Defaulting to: "
123                         + durableName);
124             }
125 
126             // Create consumer
127             consumer = jmsSupport.createConsumer(session, dest, selector, connector.isNoLocal(), durableName,
128                     topic, endpoint);
129         }
130         catch (JMSException e)
131         {
132             throw new ConnectException(e, this);
133         }
134     }
135 
136     @Override
137     protected void doStart() throws MuleException
138     {
139         try
140         {
141             // We ned to register the listener when start is called in order to only
142             // start receiving messages after start.
143             // If the consumer is null it means that the connection strategy is being
144             // run in a separate thread and hasn't managed to connect yet.
145             if (consumer == null)
146             {
147                 startOnConnect = true;
148             }
149             else
150             {
151                 startOnConnect = false;
152                 this.consumer.setMessageListener(this);
153             }
154         }
155         catch (JMSException e)
156         {
157             throw new StartException(e, this);
158         }
159     }
160 
161     @Override
162     protected void doStop() throws MuleException
163     {
164         try
165         {
166             if (consumer != null)
167             {
168                 consumer.setMessageListener(null);
169             }
170         }
171         catch (JMSException e)
172         {
173             throw new StopException(e, this);
174         }
175     }
176 
177     @Override
178     public void doDisconnect() throws Exception
179     {
180         closeConsumer();
181     }
182 
183     protected void closeConsumer()
184     {
185         connector.closeQuietly(consumer);
186         consumer = null;
187         connector.closeQuietly(session);
188         session = null;
189     }
190 
191     public void onMessage(Message message)
192     {
193         try
194         {
195             getWorkManager().scheduleWork(new MessageReceiverWorker(message, this));
196         }
197         catch (Exception e)
198         {
199             getConnector().getMuleContext().getExceptionListener().handleException(e);
200         }
201     }
202 
203     protected class MessageReceiverWorker implements Work
204     {
205         Message message;
206         MessageReceiver receiver;
207 
208         public MessageReceiverWorker(Message message, MessageReceiver receiver)
209         {
210             this.message = message;
211             this.receiver = receiver;
212         }
213 
214         public void run()
215         {
216             try
217             {
218                 TransactionTemplate<Void> tt = new TransactionTemplate<Void>(
219                                                         endpoint.getTransactionConfig(),
220                                                         connector.getMuleContext());
221 
222                 final String encoding = endpoint.getEncoding();
223 
224                 if (receiveMessagesInTransaction)
225                 {
226                     TransactionCallback<Void> cb = new MessageTransactionCallback<Void>(message)
227                     {
228 
229                         public Void doInTransaction() throws Exception
230                         {
231                             // Get Transaction & Bind MuleSession
232                             Transaction tx = TransactionCoordination.getInstance().getTransaction();
233                             if (tx != null)
234                             {
235                                 tx.bindResource(connector.getConnection(),  ReusableSessionWrapperFactory.createWrapper(session));
236                             }
237                             if (tx instanceof JmsClientAcknowledgeTransaction)
238                             {
239                                 tx.bindResource(message, message);
240                             }
241 
242                             if (logger.isDebugEnabled())
243                             {
244                                 logger.debug("Message received it is of type: " +
245                                         ClassUtils.getSimpleName(message.getClass()));
246                                 if (message.getJMSDestination() != null)
247                                 {
248                                     logger.debug("Message received on " + message.getJMSDestination() + " ("
249                                             + message.getJMSDestination().getClass().getName() + ")");
250                                 }
251                                 else
252                                 {
253                                     logger.debug("Message received on unknown destination");
254                                 }
255                                 logger.debug("Message CorrelationId is: " + message.getJMSCorrelationID());
256                                 logger.debug("Jms Message Id is: " + message.getJMSMessageID());
257                             }
258 
259                             if (message.getJMSRedelivered())
260                             {
261                                 if (logger.isDebugEnabled())
262                                 {
263                                     logger.debug("Message with correlationId: "
264                                             + message.getJMSCorrelationID()
265                                             + " is redelivered. handing off to Exception Handler");
266                                 }
267                                 redeliveryHandler.handleRedelivery(message, receiver.getEndpoint(), receiver.getFlowConstruct());
268                             }
269 
270                             MuleMessage messageToRoute = createMuleMessage(message, encoding);
271                             routeMessage(messageToRoute);
272                             return null;
273                         }
274                     };
275                     tt.execute(cb);
276                 }
277                 else
278                 {
279                     MuleMessage messageToRoute = createMuleMessage(message, encoding);
280                     routeMessage(messageToRoute);
281                 }
282             }
283             catch (Exception e)
284             {
285                 getConnector().getMuleContext().getExceptionListener().handleException(e);
286             }
287         }
288 
289         public void release()
290         {
291             // Nothing to release.
292         }
293     }
294 }