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