1
2
3
4
5
6
7
8
9
10
11 package org.mule.transport.jms;
12
13 import org.mule.DefaultMuleMessage;
14 import org.mule.api.endpoint.InboundEndpoint;
15 import org.mule.api.lifecycle.CreateException;
16 import org.mule.api.service.Service;
17 import org.mule.api.transaction.Transaction;
18 import org.mule.api.transaction.TransactionCallback;
19 import org.mule.api.transport.Connector;
20 import org.mule.api.transport.MessageAdapter;
21 import org.mule.transaction.TransactionCoordination;
22 import org.mule.transaction.TransactionTemplate;
23 import org.mule.transaction.XaTransaction;
24 import org.mule.transport.ConnectException;
25 import org.mule.transport.SingleAttemptConnectionStrategy;
26 import org.mule.transport.TransactedPollingMessageReceiver;
27 import org.mule.transport.jms.filters.JmsSelectorFilter;
28 import org.mule.util.ClassUtils;
29 import org.mule.util.MapUtils;
30
31 import java.util.Iterator;
32 import java.util.List;
33
34 import javax.jms.Destination;
35 import javax.jms.JMSException;
36 import javax.jms.Message;
37 import javax.jms.MessageConsumer;
38 import javax.jms.Session;
39
40 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
41
42 public class XaTransactedJmsMessageReceiver extends TransactedPollingMessageReceiver
43 {
44 public static final long DEFAULT_JMS_POLL_FREQUENCY = 100;
45 public static final TimeUnit DEFAULT_JMS_POLL_TIMEUNIT = TimeUnit.MILLISECONDS;
46
47 protected final JmsConnector connector;
48 protected boolean reuseConsumer;
49 protected boolean reuseSession;
50 protected final ThreadContextLocal context = new ThreadContextLocal();
51 protected final long timeout;
52 protected final RedeliveryHandler redeliveryHandler;
53
54
55
56
57 protected static class JmsThreadContext
58 {
59 public Session session;
60 public MessageConsumer consumer;
61 }
62
63
64
65
66 protected static class ThreadContextLocal extends ThreadLocal
67 {
68 public JmsThreadContext getContext()
69 {
70 return (JmsThreadContext)get();
71 }
72
73 protected Object initialValue()
74 {
75 return new JmsThreadContext();
76 }
77 }
78
79 public XaTransactedJmsMessageReceiver(Connector umoConnector, Service service, InboundEndpoint endpoint)
80 throws CreateException
81 {
82 super(umoConnector, service, endpoint);
83
84
85 this.setFrequency(DEFAULT_JMS_POLL_FREQUENCY);
86 this.setTimeUnit(DEFAULT_JMS_POLL_TIMEUNIT);
87 this.connector = (JmsConnector) umoConnector;
88 this.timeout = endpoint.getTransactionConfig().getTimeout();
89
90
91
92
93 if (this.connectionStrategy instanceof SingleAttemptConnectionStrategy)
94 {
95 this.reuseConsumer = true;
96 this.reuseSession = true;
97 }
98
99
100 this.reuseConsumer = MapUtils.getBooleanValue(endpoint.getProperties(), "reuseConsumer",
101 this.reuseConsumer);
102 this.reuseSession = MapUtils.getBooleanValue(endpoint.getProperties(), "reuseSession",
103 this.reuseSession);
104
105
106 boolean topic = connector.getTopicResolver().isTopic(getEndpoint());
107 if (topic && (reuseConsumer || reuseSession))
108 {
109 logger.warn("Destination " + getEndpoint().getEndpointURI() + " is a topic and XA transaction was " +
110 "configured. Forcing 'reuseSession' and 'reuseConsumer' to false. Set these " +
111 "on endpoint to avoid the message.");
112 reuseConsumer = false;
113 reuseSession = false;
114 }
115
116
117
118
119
120
121
122
123 this.setUseMultipleTransactedReceivers(!topic);
124
125 try
126 {
127 redeliveryHandler = this.connector.getRedeliveryHandlerFactory().create();
128 redeliveryHandler.setConnector(this.connector);
129 }
130 catch (Exception e)
131 {
132 throw new CreateException(e, this);
133 }
134 }
135
136 protected void doDispose()
137 {
138
139 }
140
141 protected void doConnect() throws Exception
142 {
143
144 }
145
146 protected void doDisconnect() throws Exception
147 {
148 if (connector.isConnected())
149 {
150
151 closeResource(true);
152 }
153 }
154
155
156
157
158 public void poll() throws Exception
159 {
160 TransactionTemplate tt = new TransactionTemplate(endpoint.getTransactionConfig(),
161 connector.getExceptionListener(), connector.getMuleContext());
162 TransactionCallback cb = new TransactionCallback()
163 {
164 public Object doInTransaction() throws Exception
165 {
166 try
167 {
168 List messages = getMessages();
169 if (messages != null && messages.size() > 0)
170 {
171 for (Iterator it = messages.iterator(); it.hasNext();)
172 {
173 processMessage(it.next());
174 }
175 }
176 return null;
177 }
178 catch (Exception e)
179 {
180
181
182 JmsThreadContext ctx = context.getContext();
183 ctx.consumer = null;
184 Transaction tx = TransactionCoordination.getInstance().getTransaction();
185 if (ctx.session != null && tx instanceof XaTransaction.MuleXaObject)
186 {
187 ((XaTransaction.MuleXaObject) ctx.session).setReuseObject(false);
188 }
189 ctx.session = null;
190 throw e;
191 }
192 }
193 };
194
195 tt.execute(cb);
196 }
197
198 protected List getMessages() throws Exception
199 {
200 Session session = this.connector.getSessionFromTransaction();
201 Transaction tx = TransactionCoordination.getInstance().getTransaction();
202 MessageConsumer consumer = createConsumer();
203
204
205 Message message = null;
206 try
207 {
208 message = consumer.receive(timeout);
209 }
210 catch (JMSException e)
211 {
212
213 if (!this.isConnected())
214 {
215
216 }
217 else
218 {
219 throw e;
220 }
221 }
222
223 if (message == null)
224 {
225 if (tx != null)
226 {
227 tx.setRollbackOnly();
228 }
229 return null;
230 }
231 message = connector.preProcessMessage(message, session);
232
233
234 if (logger.isDebugEnabled())
235 {
236 logger.debug("Message received it is of type: " +
237 ClassUtils.getSimpleName(message.getClass()));
238 if (message.getJMSDestination() != null)
239 {
240 logger.debug("Message received on " + message.getJMSDestination() + " ("
241 + message.getJMSDestination().getClass().getName() + ")");
242 }
243 else
244 {
245 logger.debug("Message received on unknown destination");
246 }
247 logger.debug("Message CorrelationId is: " + message.getJMSCorrelationID());
248 logger.debug("Jms Message Id is: " + message.getJMSMessageID());
249 }
250
251 if (message.getJMSRedelivered())
252 {
253 if (logger.isDebugEnabled())
254 {
255 logger.debug("Message with correlationId: " + message.getJMSCorrelationID()
256 + " is redelivered. handing off to Exception Handler");
257 }
258 redeliveryHandler.handleRedelivery(message);
259 }
260
261 MessageAdapter adapter = connector.getMessageAdapter(message);
262 routeMessage(new DefaultMuleMessage(adapter));
263 return null;
264 }
265
266 protected void processMessage(Object msg) throws Exception
267 {
268
269
270 }
271
272
273
274
275 protected void closeResource(boolean force)
276 {
277 JmsThreadContext ctx = context.getContext();
278 if (ctx == null)
279 {
280 return;
281 }
282
283
284 if (force || !reuseSession || !reuseConsumer)
285 {
286 connector.closeQuietly(ctx.consumer);
287 ctx.consumer = null;
288 }
289
290
291
292 if (force || !reuseSession)
293 {
294 connector.closeQuietly(ctx.session);
295 ctx.session = null;
296 }
297 }
298
299
300
301
302
303
304 protected MessageConsumer createConsumer() throws Exception
305 {
306 logger.debug("Create a consumer for the jms destination");
307 try
308 {
309 JmsSupport jmsSupport = this.connector.getJmsSupport();
310
311 JmsThreadContext ctx = context.getContext();
312 if (ctx == null)
313 {
314 ctx = new JmsThreadContext();
315 }
316
317 Session session;
318 Transaction tx = TransactionCoordination.getInstance().getTransaction();
319 if (this.reuseSession && ctx.session != null)
320 {
321 session = ctx.session;
322 tx.bindResource(this.connector.getConnection(), session);
323 }
324 else
325 {
326 session = this.connector.getSession(endpoint);
327 if (tx != null)
328 {
329 ((XaTransaction.MuleXaObject) session).setReuseObject(reuseSession);
330 }
331 }
332
333 if (reuseSession)
334 {
335 ctx.session = session;
336 }
337
338
339 if (this.reuseConsumer && ctx.consumer != null)
340 {
341 return ctx.consumer;
342 }
343
344
345 final boolean topic = connector.getTopicResolver().isTopic(endpoint);
346 Destination dest = jmsSupport.createDestination(session, endpoint.getEndpointURI()
347 .getAddress(), topic);
348
349
350 String selector = null;
351 if (endpoint.getFilter() != null && endpoint.getFilter() instanceof JmsSelectorFilter)
352 {
353 selector = ((JmsSelectorFilter)endpoint.getFilter()).getExpression();
354 }
355 else if (endpoint.getProperties() != null)
356 {
357
358
359 selector = (String)endpoint.getProperties().get(JmsConstants.JMS_SELECTOR_PROPERTY);
360 }
361 String tempDurable = (String)endpoint.getProperties().get("durable");
362 boolean durable = connector.isDurable();
363 if (tempDurable != null)
364 {
365 durable = Boolean.valueOf(tempDurable).booleanValue();
366 }
367
368
369 String durableName = (String)endpoint.getProperties().get("durableName");
370 if (durableName == null && durable && topic)
371 {
372 durableName = "mule." + connector.getName() + "." + endpoint.getEndpointURI().getAddress();
373 logger.debug("Jms Connector for this receiver is durable but no durable name has been specified. Defaulting to: "
374 + durableName);
375 }
376
377
378 MessageConsumer consumer = jmsSupport.createConsumer(session, dest, selector, connector.isNoLocal(),
379 durableName, topic);
380 if (reuseConsumer)
381 {
382 ctx.consumer = consumer;
383 }
384 return consumer;
385 }
386 catch (JMSException e)
387 {
388 throw new ConnectException(e, this);
389 }
390 }
391 }