1
2
3
4
5
6
7 package org.mule.transport.jms;
8
9 import org.mule.api.MuleEvent;
10 import org.mule.api.MuleException;
11 import org.mule.api.MuleMessage;
12 import org.mule.api.config.MuleProperties;
13 import org.mule.api.endpoint.EndpointBuilder;
14 import org.mule.api.endpoint.EndpointException;
15 import org.mule.api.endpoint.OutboundEndpoint;
16 import org.mule.api.lifecycle.InitialisationException;
17 import org.mule.api.transaction.Transaction;
18 import org.mule.api.transformer.TransformerException;
19 import org.mule.api.transport.DispatchException;
20 import org.mule.config.i18n.CoreMessages;
21 import org.mule.transaction.TransactionCoordination;
22 import org.mule.transport.AbstractMessageDispatcher;
23 import org.mule.transport.jms.i18n.JmsMessages;
24 import org.mule.util.ClassUtils;
25 import org.mule.util.NumberUtils;
26 import org.mule.util.concurrent.Latch;
27 import org.mule.util.concurrent.WaitableBoolean;
28
29 import javax.jms.DeliveryMode;
30 import javax.jms.Destination;
31 import javax.jms.JMSException;
32 import javax.jms.Message;
33 import javax.jms.MessageConsumer;
34 import javax.jms.MessageListener;
35 import javax.jms.MessageProducer;
36 import javax.jms.Session;
37 import javax.jms.TemporaryQueue;
38 import javax.jms.TemporaryTopic;
39
40 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
41
42
43
44
45
46
47
48 public class JmsMessageDispatcher extends AbstractMessageDispatcher
49 {
50
51 private JmsConnector connector;
52 private Session cachedSession;
53 private boolean disableTemporaryDestinations = false;
54 private boolean returnOriginalMessageAsReply = false;
55
56 public JmsMessageDispatcher(OutboundEndpoint endpoint)
57 {
58 super(endpoint);
59 this.connector = (JmsConnector) endpoint.getConnector();
60 disableTemporaryDestinations = connector.isDisableTemporaryReplyToDestinations() ||
61 ("true".equals(endpoint.getProperty(JmsConstants.DISABLE_TEMP_DESTINATIONS_PROPERTY)));
62 returnOriginalMessageAsReply = connector.isReturnOriginalMessageAsReply() ||
63 ("true".equals(endpoint.getProperty(JmsConstants.RETURN_ORIGINAL_MESSAGE_PROPERTY)));
64 if (returnOriginalMessageAsReply && !disableTemporaryDestinations)
65 {
66 logger.warn("The returnOriginalMessageAsReply property will be ignored because disableTemporaryReplyToDestinations=false. You need to disable temporary ReplyTo destinations in order for this propery to take effect.");
67 }
68 }
69
70 @Override
71 protected void doDispatch(MuleEvent event) throws Exception
72 {
73 if (connector.getConnection() == null)
74 {
75 throw new IllegalStateException("No JMS Connection");
76 }
77 dispatchMessage(event, false);
78 }
79
80 @Override
81 protected void doConnect() throws Exception
82 {
83
84 }
85
86 @Override
87 protected void doDisconnect() throws Exception
88 {
89
90 }
91
92 protected boolean isDisableTemporaryDestinations()
93 {
94 return disableTemporaryDestinations;
95 }
96
97 private MuleMessage dispatchMessage(MuleEvent event, boolean doSend) throws Exception
98 {
99 Session session = null;
100 MessageProducer producer = null;
101 MessageConsumer consumer = null;
102 Destination replyTo = null;
103 boolean transacted = false;
104 boolean cached = false;
105 boolean useReplyToDestination;
106
107 final Transaction muleTx = TransactionCoordination.getInstance().getTransaction();
108
109 if (logger.isDebugEnabled())
110 {
111 logger.debug("dispatching on endpoint: " + event.getEndpoint().getEndpointURI()
112 + ". MuleEvent id is: " + event.getId()
113 + ". Outbound transformers are: " + event.getEndpoint().getTransformers());
114 }
115
116
117 boolean sessionManaged = true;
118 try
119 {
120 session = connector.getSessionFromTransaction();
121 if (session != null)
122 {
123 transacted = true;
124 }
125
126
127 else if (event.getMessage().getOutboundProperty(JmsConstants.CACHE_JMS_SESSIONS_PROPERTY, connector.isCacheJmsSessions()))
128 {
129 sessionManaged = false;
130 cached = true;
131 if (cachedSession != null)
132 {
133 session = cachedSession;
134 }
135 else
136 {
137 session = connector.getSession(event.getEndpoint());
138 cachedSession = session;
139 }
140 }
141 else
142 {
143
144 sessionManaged = muleTx != null && muleTx.isXA();
145
146 session = connector.getSession(event.getEndpoint());
147 if (event.getEndpoint().getTransactionConfig().isTransacted())
148 {
149 transacted = true;
150 }
151 }
152
153
154
155 useReplyToDestination = returnResponse(event, doSend) && !transacted;
156
157 boolean topic = connector.getTopicResolver().isTopic(event.getEndpoint(), true);
158
159 Destination dest = connector.getJmsSupport().createDestination(session, endpoint);
160 producer = connector.getJmsSupport().createProducer(session, dest, topic);
161
162 Object message = event.getMessage().getPayload();
163 if (!(message instanceof Message))
164 {
165 throw new DispatchException(
166 JmsMessages.checkTransformer("JMS message", message.getClass(), connector.getName()),
167 event, (OutboundEndpoint) endpoint);
168 }
169
170 Message msg = (Message) message;
171
172 MuleMessage eventMsg = event.getMessage();
173
174 replyTo = getReplyToDestination(msg, session, event, useReplyToDestination, topic);
175
176
177 if (replyTo != null)
178 {
179 msg.setJMSReplyTo(replyTo);
180 }
181
182
183 processMessage(msg, event);
184
185
186 long ttl = eventMsg.getOutboundProperty(JmsConstants.TIME_TO_LIVE_PROPERTY, Message.DEFAULT_TIME_TO_LIVE);
187 int priority = eventMsg.getOutboundProperty(JmsConstants.PRIORITY_PROPERTY, Message.DEFAULT_PRIORITY);
188 boolean persistent= eventMsg.getOutboundProperty(JmsConstants.PERSISTENT_DELIVERY_PROPERTY, connector.isPersistentDelivery());
189
190
191 if (connector.isHonorQosHeaders())
192 {
193 Object priorityProp = eventMsg.getInboundProperty(JmsConstants.JMS_PRIORITY);
194 Object deliveryModeProp = eventMsg.getInboundProperty(JmsConstants.JMS_DELIVERY_MODE);
195
196 if (priorityProp != null)
197 {
198 priority = NumberUtils.toInt(priorityProp);
199 }
200 if (deliveryModeProp != null)
201 {
202 persistent = NumberUtils.toInt(deliveryModeProp) == DeliveryMode.PERSISTENT;
203 }
204 }
205
206 if (logger.isDebugEnabled())
207 {
208 logger.debug("Sending message of type " + ClassUtils.getSimpleName(msg.getClass()));
209 logger.debug("Sending JMS Message type " + msg.getJMSType() +
210 "\n JMSMessageID=" + msg.getJMSMessageID() +
211 "\n JMSCorrelationID=" + msg.getJMSCorrelationID() +
212 "\n JMSDeliveryMode=" + (persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT) +
213 "\n JMSPriority=" + priority +
214 "\n JMSReplyTo=" + msg.getJMSReplyTo());
215 }
216 connector.getJmsSupport().send(producer, msg, persistent, priority, ttl, topic, endpoint);
217
218 if (useReplyToDestination && replyTo != null)
219 {
220 consumer = createReplyToConsumer(msg, event, session, replyTo, topic);
221
222 if (topic)
223 {
224
225 Latch l = new Latch();
226 ReplyToListener listener = new ReplyToListener(l);
227 consumer.setMessageListener(listener);
228
229 connector.getJmsSupport().send(producer, msg, persistent, priority, ttl, topic, endpoint);
230
231 int timeout = event.getTimeout();
232
233 if (logger.isDebugEnabled())
234 {
235 logger.debug("Waiting for return event for: " + timeout + " ms on " + replyTo);
236 }
237
238 l.await(timeout, TimeUnit.MILLISECONDS);
239 consumer.setMessageListener(null);
240 listener.release();
241 Message result = listener.getMessage();
242 if (result == null)
243 {
244 logger.debug("No message was returned via replyTo destination");
245 return createNullMuleMessage();
246 }
247 else
248 {
249 return createMessageWithJmsMessagePayload(result);
250 }
251 }
252 else
253 {
254 int timeout = event.getTimeout();
255
256 if (logger.isDebugEnabled())
257 {
258 logger.debug("Waiting for return event for: " + timeout + " ms on " + replyTo);
259 }
260
261 Message result = consumer.receive(timeout);
262 if (result == null)
263 {
264 logger.debug("No message was returned via replyTo destination " + replyTo);
265 return createNullMuleMessage();
266 }
267 else
268 {
269 return createMessageWithJmsMessagePayload(result);
270 }
271 }
272 }
273 else
274 {
275
276
277
278 return returnOriginalMessageAsReply ? createMuleMessage(msg) : null;
279 }
280 }
281 finally
282 {
283 connector.closeQuietly(producer);
284 connector.closeQuietly(consumer);
285
286
287 if (replyTo != null && (replyTo instanceof TemporaryQueue || replyTo instanceof TemporaryTopic))
288 {
289 if (replyTo instanceof TemporaryQueue)
290 {
291 connector.closeQuietly((TemporaryQueue) replyTo);
292 }
293 else
294 {
295
296
297 connector.closeQuietly((TemporaryTopic) replyTo);
298 }
299 }
300
301 if (!sessionManaged && transacted) {
302 handleMultiTx(session);
303 }
304
305
306
307
308 if (session != null && !cached && !transacted)
309 {
310 connector.closeQuietly(session);
311 }
312 }
313 }
314
315 protected MuleMessage createMessageWithJmsMessagePayload(Message jmsMessage) throws Exception
316 {
317 MuleMessage muleMessage = createMuleMessage(jmsMessage);
318 Object payload = JmsMessageUtils.toObject(jmsMessage, connector.getSpecification(),
319 endpoint.getEncoding());
320 muleMessage.setPayload(payload);
321 return muleMessage;
322 }
323
324
325
326
327
328
329
330
331 protected void preTransformMessage(MuleMessage message) throws Exception
332 {
333
334 }
335
336 protected void handleMultiTx(Session session) throws Exception
337 {
338 logger.debug("Multi-transaction support is not available in Mule Community Edition.");
339 }
340
341 @Override
342 protected MuleMessage doSend(MuleEvent event) throws Exception
343 {
344 return dispatchMessage(event, true);
345 }
346
347 @Override
348 protected void doDispose()
349 {
350
351 }
352
353
354
355
356
357
358
359
360
361
362 protected void processMessage(Message msg, MuleEvent event) throws JMSException
363 {
364
365 }
366
367
368
369
370
371
372
373
374
375
376 protected boolean isHandleReplyTo(Message msg, MuleEvent event) throws JMSException
377 {
378 return connector.supportsProperty(JmsConstants.JMS_REPLY_TO);
379 }
380
381 protected MessageConsumer createReplyToConsumer(Message currentMessage, MuleEvent event,
382 Session session, Destination replyTo, boolean topic) throws JMSException
383 {
384 String selector = null;
385
386 String durableName;
387
388 if (!(replyTo instanceof TemporaryQueue || replyTo instanceof TemporaryTopic))
389 {
390 String jmsCorrelationId = currentMessage.getJMSCorrelationID();
391 if (jmsCorrelationId == null)
392 {
393 jmsCorrelationId = currentMessage.getJMSMessageID();
394 }
395
396 selector = "JMSCorrelationID='" + jmsCorrelationId + "'";
397 if (logger.isDebugEnabled())
398 {
399 logger.debug("ReplyTo Selector is: " + selector);
400 }
401 }
402
403
404 if (topic)
405 {
406 String tempDurable = (String) event.getEndpoint().getProperties().get(JmsConstants.DURABLE_PROPERTY);
407 boolean durable = connector.isDurable();
408 if (tempDurable != null)
409 {
410 durable = Boolean.valueOf(tempDurable);
411 }
412
413 durableName = (String) event.getEndpoint().getProperties().get(
414 JmsConstants.DURABLE_NAME_PROPERTY);
415 if (durableName == null && durable && topic)
416 {
417 durableName = "mule." + connector.getName() + "." + event.getEndpoint().getEndpointURI().getAddress();
418 if (logger.isDebugEnabled())
419 {
420 logger.debug("Jms Connector for this receiver is durable but no durable name has been specified. Defaulting to: " +
421 durableName);
422 }
423 }
424 }
425 return connector.getJmsSupport().createConsumer(session, replyTo, selector,
426 connector.isNoLocal(), null, topic, endpoint);
427 }
428
429 protected Destination getReplyToDestination(Message message, Session session, MuleEvent event, boolean remoteSync, boolean topic) throws JMSException, EndpointException, InitialisationException
430 {
431 Destination replyTo = null;
432
433
434 if (isHandleReplyTo(message, event))
435 {
436
437 Object tempReplyTo = event.getMessage().getOutboundProperty(JmsConstants.JMS_REPLY_TO);
438 if (tempReplyTo == null)
439 {
440
441 tempReplyTo = event.getMessage().getOutboundProperty(MuleProperties.MULE_REPLY_TO_PROPERTY);
442 if (tempReplyTo != null)
443 {
444 int i = tempReplyTo.toString().indexOf("://");
445 if (i > -1)
446 {
447 tempReplyTo = tempReplyTo.toString().substring(i+3);
448 }
449 else
450 {
451 EndpointBuilder epb = event.getMuleContext().getRegistry().lookupEndpointBuilder(tempReplyTo.toString());
452 if (epb != null)
453 {
454 tempReplyTo = epb.buildOutboundEndpoint().getEndpointURI().getAddress();
455 }
456 }
457 }
458 }
459 if (tempReplyTo != null)
460 {
461 if (tempReplyTo instanceof Destination)
462 {
463 replyTo = (Destination) tempReplyTo;
464 }
465 else
466 {
467
468 boolean replyToTopic = false;
469 String reply = tempReplyTo.toString();
470 int i = reply.indexOf(":");
471 if (i > -1)
472 {
473
474
475
476
477 String qtype = reply.substring(0, i);
478 replyToTopic = JmsConstants.TOPIC_PROPERTY.equalsIgnoreCase(qtype);
479 reply = reply.substring(i + 1);
480 }
481 replyTo = connector.getJmsSupport().createDestination(session, reply, replyToTopic, endpoint);
482 }
483 }
484
485 if (remoteSync && replyTo == null && !disableTemporaryDestinations)
486 {
487 replyTo = connector.getJmsSupport().createTemporaryDestination(session, topic);
488 }
489 }
490 return replyTo;
491
492 }
493
494 protected class ReplyToListener implements MessageListener
495 {
496 private final Latch latch;
497 private volatile Message message;
498 private final WaitableBoolean released = new WaitableBoolean(false);
499
500 public ReplyToListener(Latch latch)
501 {
502 this.latch = latch;
503 }
504
505 public Message getMessage()
506 {
507 return message;
508 }
509
510 public void release()
511 {
512 released.set(true);
513 }
514
515 public void onMessage(Message message)
516 {
517 this.message = message;
518 latch.countDown();
519 try
520 {
521 released.whenTrue(null);
522 }
523 catch (InterruptedException e)
524 {
525
526 }
527 }
528 }
529
530 @Override
531 protected void applyOutboundTransformers(MuleEvent event) throws MuleException
532 {
533 try
534 {
535 preTransformMessage(event.getMessage());
536 }
537 catch (Exception e)
538 {
539 throw new TransformerException(CoreMessages.failedToInvoke("preTransformMessage"), e);
540 }
541 super.applyOutboundTransformers(event);
542 }
543
544 }