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