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