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.transport.jms.redelivery.RedeliveryHandler;
28 import org.mule.util.ClassUtils;
29 import org.mule.util.MapUtils;
30
31 import java.util.List;
32 import java.util.concurrent.TimeUnit;
33 import java.util.concurrent.atomic.AtomicReference;
34
35 import javax.jms.Destination;
36 import javax.jms.JMSException;
37 import javax.jms.Message;
38 import javax.jms.MessageConsumer;
39 import javax.jms.Session;
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<RedeliveryHandler> redeliveryHandler = new AtomicReference<RedeliveryHandler>();
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<JmsThreadContext>
66 {
67 public JmsThreadContext getContext()
68 {
69 return get();
70 }
71
72 @Override
73 protected JmsThreadContext 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.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 @Override
167 public Void doInTransaction() throws Exception
168 {
169 try
170 {
171 List messages = getMessages();
172 if (messages != null && messages.size() > 0)
173 {
174 for (Object message : messages)
175 {
176 processMessage(message);
177 }
178 }
179 return null;
180 }
181 catch (Exception e)
182 {
183
184
185 JmsThreadContext ctx = context.getContext();
186 if (ctx.consumer != null)
187 {
188 connector.closeQuietly(ctx.consumer);
189 }
190 ctx.consumer = null;
191 Transaction tx = TransactionCoordination.getInstance().getTransaction();
192 if (ctx.session != null && tx instanceof XaTransaction.MuleXaObject)
193 {
194 if (ctx.session instanceof XaTransaction.MuleXaObject)
195 {
196 ((XaTransaction.MuleXaObject) ctx.session).setReuseObject(false);
197 }
198 else
199 {
200 logger.warn("Session should be XA, but is of type " + ctx.session.getClass().getName());
201 }
202 }
203 ctx.session = null;
204 throw e;
205 }
206 }
207 };
208
209 tt.execute(cb);
210 }
211
212 @Override
213 protected List<MuleMessage> getMessages() throws Exception
214 {
215 Session session = this.connector.getSessionFromTransaction();
216 Transaction tx = TransactionCoordination.getInstance().getTransaction();
217 MessageConsumer consumer = createConsumer();
218
219
220 Message message = null;
221 try
222 {
223 message = consumer.receive(timeout);
224 }
225 catch (JMSException e)
226 {
227
228 if (!this.isConnected())
229 {
230
231 }
232 else
233 {
234 throw e;
235 }
236 }
237
238 if (message == null)
239 {
240 if (tx != null)
241 {
242 tx.setRollbackOnly();
243 }
244 return null;
245 }
246 message = connector.preProcessMessage(message, session);
247
248
249 if (logger.isDebugEnabled())
250 {
251 logger.debug("Message received it is of type: " +
252 ClassUtils.getSimpleName(message.getClass()));
253 if (message.getJMSDestination() != null)
254 {
255 logger.debug("Message received on " + message.getJMSDestination() + " ("
256 + message.getJMSDestination().getClass().getName() + ")");
257 }
258 else
259 {
260 logger.debug("Message received on unknown destination");
261 }
262 logger.debug("Message CorrelationId is: " + message.getJMSCorrelationID());
263 logger.debug("Jms Message Id is: " + message.getJMSMessageID());
264 }
265
266 if (message.getJMSRedelivered())
267 {
268 if (logger.isDebugEnabled())
269 {
270 logger.debug("Message with correlationId: " + message.getJMSCorrelationID()
271 + " is redelivered. handing off to Exception Handler");
272 }
273 redeliveryHandler.get().handleRedelivery(message, (InboundEndpoint) endpoint, flowConstruct);
274 }
275
276 MuleMessage messageToRoute = createMuleMessage(message, endpoint.getEncoding());
277 routeMessage(messageToRoute);
278 connector.closeQuietly(consumer);
279 return null;
280 }
281
282 @Override
283 protected void processMessage(Object msg) throws Exception
284 {
285
286
287 }
288
289
290
291
292 protected void closeResource(boolean force)
293 {
294 JmsThreadContext ctx = context.getContext();
295 if (ctx == null)
296 {
297 return;
298 }
299
300
301 if (force || !reuseSession || !reuseConsumer)
302 {
303 connector.closeQuietly(ctx.consumer);
304 ctx.consumer = null;
305 }
306
307
308
309 if (force || !reuseSession)
310 {
311 connector.closeQuietly(ctx.session);
312 ctx.session = null;
313 }
314 }
315
316
317
318
319
320
321 protected MessageConsumer createConsumer() throws Exception
322 {
323 logger.debug("Create a consumer for the jms destination");
324 try
325 {
326 JmsSupport jmsSupport = this.connector.getJmsSupport();
327
328 JmsThreadContext ctx = context.getContext();
329 if (ctx == null)
330 {
331 ctx = new JmsThreadContext();
332 }
333
334 Session session;
335 Transaction tx = TransactionCoordination.getInstance().getTransaction();
336 if (this.reuseSession && ctx.session != null)
337 {
338 session = ctx.session;
339 tx.bindResource(this.connector.getConnection(), session);
340 }
341 else
342 {
343 session = this.connector.getSession(endpoint);
344 if (session != null && tx != null)
345 {
346 if (session instanceof XaTransaction.MuleXaObject)
347 {
348 ((XaTransaction.MuleXaObject) session).setReuseObject(reuseSession);
349 }
350 else
351 {
352 logger.warn("Session should be XA, but is of type " + session.getClass().getName());
353 }
354 }
355 }
356
357 if (reuseSession)
358 {
359 ctx.session = session;
360 }
361
362
363 if (this.reuseConsumer && ctx.consumer != null)
364 {
365 return ctx.consumer;
366 }
367
368
369 final boolean topic = connector.getTopicResolver().isTopic(endpoint);
370 Destination dest = jmsSupport.createDestination(session, endpoint);
371
372
373 String selector = null;
374 JmsSelectorFilter selectorFilter = connector.getSelector(endpoint);
375 if (selectorFilter != null)
376 {
377 selector = selectorFilter.getExpression();
378 }
379 else if (endpoint.getProperties() != null)
380 {
381
382
383 selector = (String)endpoint.getProperties().get(JmsConstants.JMS_SELECTOR_PROPERTY);
384 }
385 String tempDurable = (String)endpoint.getProperties().get("durable");
386 boolean durable = connector.isDurable();
387 if (tempDurable != null)
388 {
389 durable = Boolean.valueOf(tempDurable);
390 }
391
392
393 String durableName = (String)endpoint.getProperties().get("durableName");
394 if (durableName == null && durable && topic)
395 {
396 durableName = "mule." + connector.getName() + "." + endpoint.getEndpointURI().getAddress();
397 logger.debug("Jms Connector for this receiver is durable but no durable name has been specified. Defaulting to: "
398 + durableName);
399 }
400
401
402 MessageConsumer consumer = jmsSupport.createConsumer(session, dest, selector, connector.isNoLocal(),
403 durableName, topic, endpoint);
404 if (reuseConsumer)
405 {
406 ctx.consumer = consumer;
407 }
408 return consumer;
409 }
410 catch (JMSException e)
411 {
412 throw new ConnectException(e, this);
413 }
414 }
415 }