View Javadoc

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