View Javadoc

1   /*
2    * $Id: TransactedSingleResourceJmsMessageReceiver.java 19191 2010-08-25 21:05:23Z 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.MuleException;
14  import org.mule.api.MuleMessage;
15  import org.mule.api.construct.FlowConstruct;
16  import org.mule.api.endpoint.InboundEndpoint;
17  import org.mule.api.lifecycle.CreateException;
18  import org.mule.api.lifecycle.StartException;
19  import org.mule.api.lifecycle.StopException;
20  import org.mule.api.transaction.Transaction;
21  import org.mule.api.transaction.TransactionCallback;
22  import org.mule.api.transport.Connector;
23  import org.mule.transaction.TransactionCoordination;
24  import org.mule.transaction.TransactionTemplate;
25  import org.mule.transport.AbstractMessageReceiver;
26  import org.mule.transport.ConnectException;
27  import org.mule.transport.jms.filters.JmsSelectorFilter;
28  import org.mule.util.ClassUtils;
29  
30  import javax.jms.Destination;
31  import javax.jms.JMSException;
32  import javax.jms.Message;
33  import javax.jms.MessageConsumer;
34  import javax.jms.MessageListener;
35  import javax.jms.Session;
36  import javax.jms.Topic;
37  import javax.resource.spi.work.Work;
38  
39  public class TransactedSingleResourceJmsMessageReceiver extends AbstractMessageReceiver
40          implements MessageListener
41  {
42      protected JmsConnector connector;
43      protected RedeliveryHandler redeliveryHandler;
44      protected MessageConsumer consumer;
45      protected Session session;
46      protected boolean startOnConnect = false;
47  
48      /** determines whether messages will be received in a transaction template */
49      protected boolean receiveMessagesInTransaction = true;
50  
51      /** determines whether Multiple receivers are created to improve throughput */
52      protected boolean useMultipleReceivers = true;
53  
54      
55      public TransactedSingleResourceJmsMessageReceiver(Connector connector,
56                                                        FlowConstruct flowConstruct,
57                                                        InboundEndpoint endpoint) throws CreateException
58      {
59  
60          super(connector, flowConstruct, endpoint);
61  
62          this.connector = (JmsConnector) connector;
63  
64          // TODO check which properties being set in the TransecteJmsMessage receiver
65          // are needed...
66  
67          try
68          {
69              redeliveryHandler = this.connector.getRedeliveryHandlerFactory().create();
70              redeliveryHandler.setConnector(this.connector);
71          }
72          catch (Exception e)
73          {
74              throw new CreateException(e, this);
75          }
76      }
77  
78      @Override
79      protected void doDispose()
80      {
81          // template method
82      }
83  
84      @Override
85      protected void doConnect() throws Exception
86      {
87          try
88          {
89              JmsSupport jmsSupport = this.connector.getJmsSupport();
90              // Create session if none exists
91              if (session == null)
92              {
93                  session = this.connector.getSession(endpoint);
94              }
95  
96              // Create destination
97              boolean topic = connector.getTopicResolver().isTopic(endpoint);
98  
99              Destination dest = jmsSupport.createDestination(session, endpoint);
100             // Extract jms selector
101             String selector = null;
102             if (endpoint.getFilter() != null && endpoint.getFilter() instanceof JmsSelectorFilter)
103             {
104                 selector = ((JmsSelectorFilter) endpoint.getFilter()).getExpression();
105             }
106             else if (endpoint.getProperties() != null)
107             {
108                 // still allow the selector to be set as a property on the endpoint
109                 // to be backward compatable
110                 selector = (String) endpoint.getProperties().get(JmsConstants.JMS_SELECTOR_PROPERTY);
111             }
112             String tempDurable = (String) endpoint.getProperties().get(JmsConstants.DURABLE_PROPERTY);
113             boolean durable = connector.isDurable();
114             if (tempDurable != null)
115             {
116                 durable = Boolean.valueOf(tempDurable).booleanValue();
117             }
118 
119             // Get the durable subscriber name if there is one
120             String durableName = (String) endpoint.getProperties().get(JmsConstants.DURABLE_NAME_PROPERTY);
121             if (durableName == null && durable && dest instanceof Topic)
122             {
123                 durableName = "mule." + connector.getName() + "." + endpoint.getEndpointURI().getAddress();
124                 logger.debug("Jms Connector for this receiver is durable but no durable name has been specified. Defaulting to: "
125                         + durableName);
126             }
127 
128             // Create consumer
129             consumer = jmsSupport.createConsumer(session, dest, selector, connector.isNoLocal(), durableName,
130                     topic, endpoint);
131         }
132         catch (JMSException e)
133         {
134             throw new ConnectException(e, this);
135         }
136     }
137 
138     @Override
139     protected void doStart() throws MuleException
140     {
141         try
142         {
143             // We ned to register the listener when start is called in order to only
144             // start receiving messages after start.
145             // If the consumer is null it means that the connection strategy is being
146             // run in a separate thread and hasn't managed to connect yet.
147             if (consumer == null)
148             {
149                 startOnConnect = true;
150             }
151             else
152             {
153                 startOnConnect = false;
154                 this.consumer.setMessageListener(this);
155             }
156         }
157         catch (JMSException e)
158         {
159             throw new StartException(e, this);
160         }
161     }
162 
163     @Override
164     protected void doStop() throws MuleException
165     {
166         try
167         {
168             if (consumer != null)
169             {
170                 consumer.setMessageListener(null);
171             }
172         }
173         catch (JMSException e)
174         {
175             throw new StopException(e, this);
176         }
177     }
178 
179     @Override
180     public void doDisconnect() throws Exception
181     {
182         closeConsumer();
183     }
184 
185     protected void closeConsumer()
186     {
187         connector.closeQuietly(consumer);
188         consumer = null;
189         connector.closeQuietly(session);
190         session = null;
191     }
192 
193     public void onMessage(Message message)
194     {
195         try
196         {
197             getWorkManager().scheduleWork(new MessageReceiverWorker(message));
198         }
199         catch (Exception e)
200         {
201             getConnector().getMuleContext().getExceptionListener().handleException(e);
202         }
203     }
204 
205     protected class MessageReceiverWorker implements Work
206     {
207         Message message;
208 
209         public MessageReceiverWorker(Message message)
210         {
211             this.message = message;
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(), 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);
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 }