1
2
3
4
5
6
7
8
9
10
11 package org.mule.transport.jms;
12
13 import org.mule.api.MuleException;
14 import org.mule.api.MuleRuntimeException;
15 import org.mule.api.construct.FlowConstruct;
16 import org.mule.api.endpoint.InboundEndpoint;
17 import org.mule.api.lifecycle.CreateException;
18 import org.mule.api.lifecycle.LifecycleException;
19 import org.mule.api.transaction.Transaction;
20 import org.mule.api.transaction.TransactionException;
21 import org.mule.api.transport.Connector;
22 import org.mule.config.i18n.MessageFactory;
23 import org.mule.transaction.TransactionCollection;
24 import org.mule.transport.AbstractMessageReceiver;
25 import org.mule.transport.AbstractReceiverWorker;
26 import org.mule.transport.ConnectException;
27 import org.mule.transport.jms.filters.JmsSelectorFilter;
28 import org.mule.transport.jms.redelivery.RedeliveryHandler;
29 import org.mule.util.ClassUtils;
30
31 import java.util.ArrayList;
32 import java.util.Iterator;
33 import java.util.List;
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.MessageListener;
40 import javax.jms.Session;
41 import javax.resource.spi.work.WorkException;
42
43 import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
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 @Override
164 protected boolean isDoStartMustFollowDoConnect()
165 {
166 return true;
167 }
168
169 private class SubReceiver implements MessageListener
170 {
171 private final Log subLogger = LogFactory.getLog(getClass());
172
173 private volatile Session session;
174 private volatile MessageConsumer consumer;
175
176 protected volatile boolean connected;
177 protected volatile boolean started;
178
179 protected void doConnect() throws MuleException
180 {
181 subLogger.debug("SUB doConnect()");
182 try
183 {
184 createConsumer();
185 }
186 catch (Exception e)
187 {
188 throw new LifecycleException(e, this);
189 }
190 connected = true;
191 }
192
193 protected void doDisconnect() throws MuleException
194 {
195 subLogger.debug("SUB doDisconnect()");
196 if (started)
197 {
198 doStop(true);
199 }
200 closeConsumer();
201 connected = false;
202 }
203
204 protected void closeConsumer()
205 {
206 jmsConnector.closeQuietly(consumer);
207 consumer = null;
208 jmsConnector.closeQuietly(session);
209 session = null;
210 }
211
212 protected void doStart() throws MuleException
213 {
214 subLogger.debug("SUB doStart()");
215 if (!connected)
216 {
217 doConnect();
218 }
219
220 try
221 {
222 consumer.setMessageListener(this);
223 started = true;
224 }
225 catch (JMSException e)
226 {
227 throw new LifecycleException(e, this);
228 }
229 }
230
231
232
233
234
235
236 protected void doStop(boolean force) throws MuleException
237 {
238 subLogger.debug("SUB doStop()");
239
240 if (consumer != null)
241 {
242 try
243 {
244 consumer.setMessageListener(null);
245 started = false;
246 }
247 catch (JMSException e)
248 {
249 if (force)
250 {
251 logger.warn("Unable to cleanly stop subreceiver: " + e.getMessage());
252 started = false;
253 }
254 else
255 {
256 throw new LifecycleException(e, this);
257 }
258 }
259 }
260 }
261
262
263
264
265 protected void createConsumer() throws Exception
266 {
267 subLogger.debug("SUB createConsumer()");
268
269 try
270 {
271 JmsSupport jmsSupport = jmsConnector.getJmsSupport();
272 boolean topic = jmsConnector.getTopicResolver().isTopic(endpoint, true);
273
274
275 if (session == null)
276 {
277 session = jmsConnector.getSession(endpoint);
278 }
279
280
281 Destination dest = jmsSupport.createDestination(session, endpoint);
282
283
284 String selector = null;
285 if (endpoint.getFilter() != null && endpoint.getFilter() instanceof JmsSelectorFilter)
286 {
287 selector = ((JmsSelectorFilter) endpoint.getFilter()).getExpression();
288 }
289 else
290 {
291 if (endpoint.getProperties() != null)
292 {
293
294
295 selector = (String) endpoint.getProperties().get(JmsConstants.JMS_SELECTOR_PROPERTY);
296 }
297 }
298 String tempDurable = (String) endpoint.getProperties().get(JmsConstants.DURABLE_PROPERTY);
299 boolean durable = jmsConnector.isDurable();
300 if (tempDurable != null)
301 {
302 durable = Boolean.valueOf(tempDurable);
303 }
304
305
306 String durableName = (String) endpoint.getProperties().get(JmsConstants.DURABLE_NAME_PROPERTY);
307 if (durableName == null && durable && topic)
308 {
309 durableName = "mule." + jmsConnector.getName() + "." + endpoint.getEndpointURI().getAddress();
310 logger.debug("Jms Connector for this receiver is durable but no durable name has been specified. Defaulting to: "
311 + durableName);
312 }
313
314
315 consumer = jmsSupport.createConsumer(session, dest, selector, jmsConnector.isNoLocal(), durableName,
316 topic, endpoint);
317 }
318 catch (JMSException e)
319 {
320 throw new ConnectException(e, MultiConsumerJmsMessageReceiver.this);
321 }
322 }
323
324 public void onMessage(final Message message)
325 {
326 try
327 {
328
329
330
331
332 getWorkManager().doWork(new JmsWorker(message, MultiConsumerJmsMessageReceiver.this, this));
333 }
334 catch (WorkException e)
335 {
336 throw new MuleRuntimeException(MessageFactory.createStaticMessage(
337 "Couldn't submit a work item to the WorkManager"), e);
338 }
339 }
340 }
341
342 protected class JmsWorker extends AbstractReceiverWorker
343 {
344 private final SubReceiver subReceiver;
345
346 public JmsWorker(Message message, AbstractMessageReceiver receiver, SubReceiver subReceiver)
347 {
348 super(new ArrayList<Object>(1), receiver);
349 this.subReceiver = subReceiver;
350 messages.add(message);
351 }
352
353 @Override
354 protected Object preProcessMessage(Object message) throws Exception
355 {
356 Message m = (Message) message;
357
358 if (logger.isDebugEnabled())
359 {
360 logger.debug("Message received it is of type: " +
361 ClassUtils.getSimpleName(message.getClass()));
362 if (m.getJMSDestination() != null)
363 {
364 logger.debug("Message received on " + m.getJMSDestination() + " ("
365 + m.getJMSDestination().getClass().getName() + ")");
366 }
367 else
368 {
369 logger.debug("Message received on unknown destination");
370 }
371 logger.debug("Message CorrelationId is: " + m.getJMSCorrelationID());
372 logger.debug("Jms Message Id is: " + m.getJMSMessageID());
373 }
374
375 if (m.getJMSRedelivered())
376 {
377
378 RedeliveryHandler redeliveryHandler = jmsConnector.getRedeliveryHandlerFactory().create();
379 redeliveryHandler.setConnector(jmsConnector);
380 if (logger.isDebugEnabled())
381 {
382 logger.debug("Message with correlationId: " + m.getJMSCorrelationID()
383 + " has redelivered flag set, handing off to Redelivery Handler");
384 }
385 redeliveryHandler.handleRedelivery(m, receiver.getEndpoint(), receiver.getFlowConstruct());
386 }
387 return m;
388
389 }
390
391 @Override
392 protected void bindTransaction(Transaction tx) throws TransactionException
393 {
394 if (tx instanceof JmsTransaction || tx instanceof TransactionCollection)
395 {
396 if (logger.isDebugEnabled())
397 {
398 logger.debug("Binding " + subReceiver.session + " to " + jmsConnector.getConnection());
399 }
400 tx.bindResource(jmsConnector.getConnection(), subReceiver.session);
401 }
402 else
403 {
404 if (tx instanceof JmsClientAcknowledgeTransaction)
405 {
406
407
408
409 ((JmsClientAcknowledgeTransaction) tx).setMessage((Message) messages.get(0));
410 }
411 }
412 }
413 }
414
415 }