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