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