1
2
3
4
5
6
7
8
9
10
11 package org.mule.transport.jms;
12
13 import org.mule.MessageExchangePattern;
14 import org.mule.api.MessagingException;
15 import org.mule.api.MuleException;
16 import org.mule.api.construct.FlowConstruct;
17 import org.mule.api.endpoint.InboundEndpoint;
18 import org.mule.api.exception.RollbackSourceCallback;
19 import org.mule.api.lifecycle.CreateException;
20 import org.mule.api.lifecycle.LifecycleException;
21 import org.mule.api.transaction.Transaction;
22 import org.mule.api.transaction.TransactionException;
23 import org.mule.api.transport.Connector;
24 import org.mule.api.transport.ReplyToHandler;
25 import org.mule.transaction.TransactionCollection;
26 import org.mule.transport.AbstractMessageReceiver;
27 import org.mule.transport.AbstractReceiverWorker;
28 import org.mule.transport.ConnectException;
29 import org.mule.transport.jms.filters.JmsSelectorFilter;
30 import org.mule.transport.jms.redelivery.RedeliveryHandler;
31 import org.mule.util.ClassUtils;
32
33 import java.util.ArrayList;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.concurrent.CopyOnWriteArrayList;
37
38 import javax.jms.Destination;
39 import javax.jms.JMSException;
40 import javax.jms.Message;
41 import javax.jms.MessageConsumer;
42 import javax.jms.MessageListener;
43 import javax.jms.Session;
44
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogFactory;
47
48
49
50
51
52
53 public class MultiConsumerJmsMessageReceiver extends AbstractMessageReceiver
54 {
55 protected final List<SubReceiver> consumers;
56
57 protected final int receiversCount;
58
59 private final JmsConnector jmsConnector;
60
61 public MultiConsumerJmsMessageReceiver(Connector connector, FlowConstruct flowConstruct, InboundEndpoint endpoint)
62 throws CreateException
63 {
64 super(connector, flowConstruct, endpoint);
65
66 jmsConnector = (JmsConnector) connector;
67
68 final boolean isTopic = jmsConnector.getTopicResolver().isTopic(endpoint, true);
69 if (isTopic && jmsConnector.getNumberOfConsumers() != 1)
70 {
71 if (logger.isInfoEnabled())
72 {
73 logger.info("Destination " + getEndpoint().getEndpointURI() + " is a topic, but " + jmsConnector.getNumberOfConsumers() +
74 " receivers have been requested. Will configure only 1.");
75 }
76 receiversCount = 1;
77 }
78 else
79 {
80 receiversCount = jmsConnector.getNumberOfConsumers();
81 }
82 if (logger.isDebugEnabled())
83 {
84 logger.debug("Creating " + receiversCount + " sub-receivers for " + endpoint.getEndpointURI());
85 }
86
87 consumers = new CopyOnWriteArrayList();
88 }
89
90 @Override
91 protected void doStart() throws MuleException
92 {
93 logger.debug("doStart()");
94 SubReceiver sub;
95 for (Iterator<SubReceiver> it = consumers.iterator(); it.hasNext();)
96 {
97 sub = it.next();
98 sub.doStart();
99 }
100 }
101
102 @Override
103 protected void doStop() throws MuleException
104 {
105 logger.debug("doStop()");
106 if (consumers != null)
107 {
108 SubReceiver sub;
109 for (Iterator<SubReceiver> it = consumers.iterator(); it.hasNext();)
110 {
111 sub = it.next();
112 sub.doStop(true);
113 }
114 }
115 }
116
117 @Override
118 protected void doConnect() throws Exception
119 {
120 logger.debug("doConnect()");
121
122 if (!consumers.isEmpty())
123 {
124 throw new IllegalStateException("List should be empty, there may be a concurrency issue here (see EE-1275)");
125 }
126
127 SubReceiver sub;
128 for (int i = 0; i < receiversCount; i++)
129 {
130 sub = new SubReceiver();
131 sub.doConnect();
132 consumers.add(sub);
133 }
134 }
135
136 @Override
137 protected void doDisconnect() throws Exception
138 {
139 logger.debug("doDisconnect()");
140
141 SubReceiver sub;
142 for (Iterator<SubReceiver> it = consumers.iterator(); it.hasNext();)
143 {
144 sub = it.next();
145 try
146 {
147 sub.doDisconnect();
148 }
149 finally
150 {
151 sub = null;
152 }
153 }
154 consumers.clear();
155 }
156
157 @Override
158 protected void doDispose()
159 {
160 logger.debug("doDispose()");
161 }
162
163 private class SubReceiver implements MessageListener
164 {
165 private final Log subLogger = LogFactory.getLog(getClass());
166
167 private volatile Session session;
168 private volatile MessageConsumer consumer;
169
170 protected volatile boolean connected;
171 protected volatile boolean started;
172
173 protected void doConnect() throws MuleException
174 {
175 subLogger.debug("SUB doConnect()");
176 try
177 {
178 createConsumer();
179 }
180 catch (Exception e)
181 {
182 throw new LifecycleException(e, this);
183 }
184 connected = true;
185 }
186
187 protected void doDisconnect() throws MuleException
188 {
189 subLogger.debug("SUB doDisconnect()");
190 if (started)
191 {
192 doStop(true);
193 }
194 closeConsumer();
195 connected = false;
196 }
197
198 protected void closeConsumer()
199 {
200 jmsConnector.closeQuietly(consumer);
201 consumer = null;
202 jmsConnector.closeQuietly(session);
203 session = null;
204 }
205
206 protected void doStart() throws MuleException
207 {
208 subLogger.debug("SUB doStart()");
209 if (!connected)
210 {
211 doConnect();
212 }
213
214 try
215 {
216 consumer.setMessageListener(this);
217 started = true;
218 }
219 catch (JMSException e)
220 {
221 throw new LifecycleException(e, this);
222 }
223 }
224
225
226
227
228
229
230 protected void doStop(boolean force) throws MuleException
231 {
232 subLogger.debug("SUB doStop()");
233
234 if (consumer != null)
235 {
236 try
237 {
238 consumer.setMessageListener(null);
239 started = false;
240 }
241 catch (JMSException e)
242 {
243 if (force)
244 {
245 logger.warn("Unable to cleanly stop subreceiver: " + e.getMessage());
246 started = false;
247 }
248 else
249 {
250 throw new LifecycleException(e, this);
251 }
252 }
253 }
254 }
255
256
257
258
259 protected void createConsumer() throws Exception
260 {
261 subLogger.debug("SUB createConsumer()");
262
263 try
264 {
265 JmsSupport jmsSupport = jmsConnector.getJmsSupport();
266 boolean topic = jmsConnector.getTopicResolver().isTopic(endpoint, true);
267
268
269 if (session == null)
270 {
271 session = jmsConnector.getSession(endpoint);
272 }
273
274
275 Destination dest = jmsSupport.createDestination(session, endpoint);
276
277
278 String selector = null;
279 JmsSelectorFilter selectorFilter = jmsConnector.getSelector(endpoint);
280 if (selectorFilter != null)
281 {
282 selector = selectorFilter.getExpression();
283 }
284 else
285 {
286 if (endpoint.getProperties() != null)
287 {
288
289
290 selector = (String) endpoint.getProperties().get(JmsConstants.JMS_SELECTOR_PROPERTY);
291 }
292 }
293 String tempDurable = (String) endpoint.getProperties().get(JmsConstants.DURABLE_PROPERTY);
294 boolean durable = jmsConnector.isDurable();
295 if (tempDurable != null)
296 {
297 durable = Boolean.valueOf(tempDurable);
298 }
299
300
301 String durableName = (String) endpoint.getProperties().get(JmsConstants.DURABLE_NAME_PROPERTY);
302 if (durableName == null && durable && topic)
303 {
304 durableName = "mule." + jmsConnector.getName() + "." + endpoint.getEndpointURI().getAddress();
305 logger.debug("Jms Connector for this receiver is durable but no durable name has been specified. Defaulting to: "
306 + durableName);
307 }
308
309
310 consumer = jmsSupport.createConsumer(session, dest, selector, jmsConnector.isNoLocal(), durableName,
311 topic, endpoint);
312 }
313 catch (JMSException e)
314 {
315 throw new ConnectException(e, MultiConsumerJmsMessageReceiver.this);
316 }
317 }
318
319 public void onMessage(final Message message)
320 {
321 try
322 {
323
324 JmsWorker worker = new JmsWorker(message, MultiConsumerJmsMessageReceiver.this, this);
325 worker.processMessages();
326 }
327 catch (Exception e)
328 {
329
330 RollbackSourceCallback rollbackMethod = new RollbackSourceCallback()
331 {
332 public void rollback()
333 {
334 try
335 {
336 session.recover();
337 }
338 catch (JMSException jmsEx)
339 {
340 logger.error(jmsEx);
341 }
342 }
343 };
344
345 if (e instanceof MessagingException)
346 {
347 getFlowConstruct().getExceptionListener().handleException(e, ((MessagingException) e).getEvent(), rollbackMethod);
348 }
349 else
350 {
351 getConnector().getMuleContext().getExceptionListener().handleException(e, rollbackMethod);
352 }
353 }
354 }
355 }
356
357 protected class JmsWorker extends AbstractReceiverWorker
358 {
359 private final SubReceiver subReceiver;
360
361 public JmsWorker(Message message, AbstractMessageReceiver receiver, SubReceiver subReceiver)
362 {
363 super(new ArrayList<Object>(1), receiver);
364 this.subReceiver = subReceiver;
365 messages.add(message);
366 }
367
368 @Override
369 protected Object preProcessMessage(Object message) throws Exception
370 {
371 Message m = (Message) message;
372
373 if (logger.isDebugEnabled())
374 {
375 logger.debug("Message received it is of type: " +
376 ClassUtils.getSimpleName(message.getClass()));
377 if (m.getJMSDestination() != null)
378 {
379 logger.debug("Message received on " + m.getJMSDestination() + " ("
380 + m.getJMSDestination().getClass().getName() + ")");
381 }
382 else
383 {
384 logger.debug("Message received on unknown destination");
385 }
386 logger.debug("Message CorrelationId is: " + m.getJMSCorrelationID());
387 logger.debug("Jms Message Id is: " + m.getJMSMessageID());
388 }
389
390 if (m.getJMSRedelivered())
391 {
392
393 RedeliveryHandler redeliveryHandler = jmsConnector.getRedeliveryHandlerFactory().create();
394 redeliveryHandler.setConnector(jmsConnector);
395 if (logger.isDebugEnabled())
396 {
397 logger.debug("Message with correlationId: " + m.getJMSCorrelationID()
398 + " has redelivered flag set, handing off to Redelivery Handler");
399 }
400 redeliveryHandler.handleRedelivery(m, receiver.getEndpoint(), receiver.getFlowConstruct());
401 }
402 return m;
403
404 }
405
406 @Override
407 protected void bindTransaction(Transaction tx) throws TransactionException
408 {
409 if (tx instanceof JmsTransaction || tx instanceof TransactionCollection)
410 {
411 if (logger.isDebugEnabled())
412 {
413 logger.debug("Binding " + subReceiver.session + " to " + jmsConnector.getConnection());
414 }
415 tx.bindResource(jmsConnector.getConnection(), ReusableSessionWrapperFactory.createWrapper(subReceiver.session));
416 }
417 else
418 {
419 if (tx instanceof JmsClientAcknowledgeTransaction)
420 {
421
422
423
424 ((JmsClientAcknowledgeTransaction) tx).setMessage((Message) messages.get(0));
425 }
426 }
427 }
428 }
429
430 }