001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.gravity.adapters;
022
023 import java.io.ByteArrayInputStream;
024 import java.io.ByteArrayOutputStream;
025 import java.io.IOException;
026 import java.io.Serializable;
027 import java.util.Date;
028 import java.util.Enumeration;
029 import java.util.HashMap;
030 import java.util.Map;
031 import java.util.Properties;
032
033 import javax.jms.ConnectionFactory;
034 import javax.jms.Destination;
035 import javax.jms.JMSException;
036 import javax.jms.MessageListener;
037 import javax.jms.ObjectMessage;
038 import javax.jms.Session;
039 import javax.jms.TextMessage;
040 import javax.naming.Context;
041 import javax.naming.InitialContext;
042 import javax.naming.NamingException;
043
044 import org.granite.clustering.GraniteDistributedDataFactory;
045 import org.granite.clustering.TransientReference;
046 import org.granite.context.GraniteContext;
047 import org.granite.gravity.Channel;
048 import org.granite.gravity.Gravity;
049 import org.granite.gravity.MessageReceivingException;
050 import org.granite.logging.Logger;
051 import org.granite.messaging.amf.io.AMF3Deserializer;
052 import org.granite.messaging.amf.io.AMF3Serializer;
053 import org.granite.messaging.service.ServiceException;
054 import org.granite.messaging.webapp.HttpGraniteContext;
055 import org.granite.util.XMap;
056
057 import flex.messaging.messages.AcknowledgeMessage;
058 import flex.messaging.messages.AsyncMessage;
059 import flex.messaging.messages.CommandMessage;
060 import flex.messaging.messages.ErrorMessage;
061
062 /**
063 * @author William DRAI
064 */
065 public class JMSServiceAdapter extends ServiceAdapter {
066
067 private static final Logger log = Logger.getLogger(JMSServiceAdapter.class);
068
069 public static final long DEFAULT_FAILOVER_RETRY_INTERVAL = 1000L;
070 public static final int DEFAULT_FAILOVER_RETRY_COUNT = 4;
071
072 protected ConnectionFactory jmsConnectionFactory = null;
073 protected javax.jms.Destination jmsDestination = null;
074 protected Map<String, JMSClient> jmsClients = new HashMap<String, JMSClient>();
075 protected String destinationName = null;
076 protected boolean textMessages = false;
077 protected boolean transactedSessions = false;
078 protected int acknowledgeMode = Session.AUTO_ACKNOWLEDGE;
079 protected int messagePriority = javax.jms.Message.DEFAULT_PRIORITY;
080 protected int deliveryMode = javax.jms.Message.DEFAULT_DELIVERY_MODE;
081 protected boolean noLocal = false;
082 protected boolean sessionSelector = false;
083
084 protected long failoverRetryInterval = DEFAULT_FAILOVER_RETRY_INTERVAL;
085 protected int failoverRetryCount = DEFAULT_FAILOVER_RETRY_COUNT;
086
087 @Override
088 public void configure(XMap adapterProperties, XMap destinationProperties) throws ServiceException {
089 super.configure(adapterProperties, destinationProperties);
090
091 log.info("Using JMS configuration: %s", destinationProperties.getOne("jms"));
092
093 destinationName = destinationProperties.get("jms/destination-name");
094
095 if (Boolean.TRUE.toString().equals(destinationProperties.get("jms/transacted-sessions")))
096 transactedSessions = true;
097
098 String ackMode = destinationProperties.get("jms/acknowledge-mode");
099 if ("AUTO_ACKNOWLEDGE".equals(ackMode))
100 acknowledgeMode = Session.AUTO_ACKNOWLEDGE;
101 else if ("CLIENT_ACKNOWLEDGE".equals(ackMode))
102 acknowledgeMode = Session.CLIENT_ACKNOWLEDGE;
103 else if ("DUPS_OK_ACKNOWLEDGE".equals(ackMode))
104 acknowledgeMode = Session.DUPS_OK_ACKNOWLEDGE;
105 else if (ackMode != null)
106 log.warn("Unsupported acknowledge mode: %s (using default AUTO_ACKNOWLEDGE)", ackMode);
107
108 if ("javax.jms.TextMessage".equals(destinationProperties.get("jms/message-type")))
109 textMessages = true;
110
111 if (Boolean.TRUE.toString().equals(destinationProperties.get("jms/no-local")))
112 noLocal = true;
113
114 if (Boolean.TRUE.toString().equals(destinationProperties.get("session-selector")))
115 sessionSelector = true;
116
117 failoverRetryInterval = destinationProperties.get("jms/failover-retry-interval", Long.TYPE, DEFAULT_FAILOVER_RETRY_INTERVAL);
118 if (failoverRetryInterval <= 0) {
119 log.warn("Illegal failover retry interval: %d (using default %d)", failoverRetryInterval, DEFAULT_FAILOVER_RETRY_INTERVAL);
120 failoverRetryInterval = DEFAULT_FAILOVER_RETRY_INTERVAL;
121 }
122
123 failoverRetryCount = destinationProperties.get("jms/failover-retry-count", Integer.TYPE, DEFAULT_FAILOVER_RETRY_COUNT);
124 if (failoverRetryCount <= 0) {
125 log.warn("Illegal failover retry count: %s (using default %d)", failoverRetryCount, DEFAULT_FAILOVER_RETRY_COUNT);
126 failoverRetryCount = DEFAULT_FAILOVER_RETRY_COUNT;
127 }
128
129 Properties environment = new Properties();
130 for (XMap property : destinationProperties.getAll("jms/initial-context-environment/property")) {
131 String name = property.get("name");
132 String value = property.get("value");
133
134 if ("Context.PROVIDER_URL".equals(name))
135 environment.put(Context.PROVIDER_URL, value);
136 else if ("Context.INITIAL_CONTEXT_FACTORY".equals(name))
137 environment.put(Context.INITIAL_CONTEXT_FACTORY, value);
138 else if ("Context.URL_PKG_PREFIXES".equals(name))
139 environment.put(Context.URL_PKG_PREFIXES, value);
140 else if ("Context.SECURITY_PRINCIPAL".equals(name))
141 environment.put(Context.SECURITY_PRINCIPAL, value);
142 else if ("Context.SECURITY_CREDENTIALS".equals(name))
143 environment.put(Context.SECURITY_CREDENTIALS, value);
144 else
145 log.warn("Unknown InitialContext property: %s (ignored)", name);
146 }
147
148 InitialContext initialContext = null;
149 try {
150 initialContext = new InitialContext(environment.size() > 0 ? environment : null);
151 }
152 catch (NamingException e) {
153 log.error(e, "Could not initialize JNDI context");
154 throw new ServiceException("Error configuring JMS Adapter", e);
155 }
156
157 String cfJndiName = destinationProperties.get("jms/connection-factory");
158 try {
159 jmsConnectionFactory = (ConnectionFactory)initialContext.lookup(cfJndiName);
160 }
161 catch (NamingException e) {
162 log.error(e, "Could not find JMS ConnectionFactory named %s in JNDI", cfJndiName);
163 throw new ServiceException("Error configuring JMS Adapter", e);
164 }
165
166 String dsJndiName = destinationProperties.get("jms/destination-jndi-name");
167 try {
168 jmsDestination = (Destination)initialContext.lookup(dsJndiName);
169 }
170 catch (NamingException e) {
171 log.error(e, "Could not find JMS destination named %s in JNDI", dsJndiName);
172 throw new ServiceException("Error configuring JMS Adapter", e);
173 }
174 }
175
176 protected javax.jms.Destination getProducerDestination(String topic) {
177 return jmsDestination;
178 }
179
180 protected javax.jms.Destination getConsumerDestination(String topic) {
181 return jmsDestination;
182 }
183
184 @Override
185 public void start() throws ServiceException {
186 super.start();
187 }
188
189 @Override
190 public void stop() throws ServiceException {
191 super.stop();
192
193 for (JMSClient jmsClient : jmsClients.values()) {
194 try {
195 jmsClient.close();
196 }
197 catch (Exception e) {
198 log.warn(e, "Could not close JMSClient: %s", jmsClient);
199 }
200 }
201 jmsClients.clear();
202 }
203
204
205 private synchronized JMSClient connectJMSClient(Channel client, String destination) throws Exception {
206 JMSClient jmsClient = jmsClients.get(client.getId());
207 if (jmsClient == null) {
208 jmsClient = new JMSClientImpl(client);
209 jmsClient.connect();
210 jmsClients.put(client.getId(), jmsClient);
211 if (sessionSelector && GraniteContext.getCurrentInstance() instanceof HttpGraniteContext)
212 ((HttpGraniteContext)GraniteContext.getCurrentInstance()).getSessionMap().put(JMSClient.JMSCLIENT_KEY_PREFIX + destination, jmsClient);
213 log.debug("JMS client connected for channel " + client.getId());
214 }
215 return jmsClient;
216 }
217
218 private synchronized void closeJMSClientIfNecessary(Channel client, String destination) throws Exception {
219 JMSClient jmsClient = jmsClients.get(client.getId());
220 if (jmsClient != null && !jmsClient.hasActiveConsumer()) {
221 jmsClient.close();
222 jmsClients.remove(client.getId());
223 if (sessionSelector && GraniteContext.getCurrentInstance() instanceof HttpGraniteContext)
224 ((HttpGraniteContext)GraniteContext.getCurrentInstance()).getSessionMap().remove(JMSClient.JMSCLIENT_KEY_PREFIX + destination);
225 log.debug("JMS client closed for channel " + client.getId());
226 }
227 }
228
229 @Override
230 public Object invoke(Channel fromClient, AsyncMessage message) {
231 try {
232 JMSClient jmsClient = connectJMSClient(fromClient, message.getDestination());
233 jmsClient.send(message);
234
235 AsyncMessage reply = new AcknowledgeMessage(message);
236 reply.setMessageId(message.getMessageId());
237
238 return reply;
239 }
240 catch (Exception e) {
241 log.error(e, "Error sending message");
242 ErrorMessage error = new ErrorMessage(message, null);
243 error.setFaultString("JMS Adapter error " + e.getMessage());
244
245 return error;
246 }
247 }
248
249 @Override
250 public Object manage(Channel fromChannel, CommandMessage message) {
251 if (message.getOperation() == CommandMessage.SUBSCRIBE_OPERATION) {
252 try {
253 JMSClient jmsClient = connectJMSClient(fromChannel, message.getDestination());
254 jmsClient.subscribe(message);
255
256 AsyncMessage reply = new AcknowledgeMessage(message);
257 return reply;
258 }
259 catch (Exception e) {
260 throw new RuntimeException("JMSAdapter subscribe error on topic: " + message, e);
261 }
262 }
263 else if (message.getOperation() == CommandMessage.UNSUBSCRIBE_OPERATION) {
264 try {
265 JMSClient jmsClient = connectJMSClient(fromChannel, message.getDestination());
266 jmsClient.unsubscribe(message);
267 closeJMSClientIfNecessary(fromChannel, message.getDestination());
268
269 AsyncMessage reply = new AcknowledgeMessage(message);
270 return reply;
271 }
272 catch (Exception e) {
273 throw new RuntimeException("JMSAdapter unsubscribe error on topic: " + message, e);
274 }
275 }
276
277 return null;
278 }
279
280
281 @TransientReference
282 private class JMSClientImpl implements JMSClient {
283
284 private Channel channel = null;
285 private String topic = null;
286 private javax.jms.Connection jmsConnection = null;
287 private javax.jms.Session jmsProducerSession = null;
288 private javax.jms.MessageProducer jmsProducer = null;
289 private Map<String, JMSConsumer> consumers = new HashMap<String, JMSConsumer>();
290 private boolean useGlassFishNoCommitWorkaround = false;
291
292
293 public JMSClientImpl(Channel channel) {
294 this.channel = channel;
295 }
296
297 public boolean hasActiveConsumer() {
298 return consumers != null && !consumers.isEmpty();
299 }
300
301
302 public void connect() throws ServiceException {
303 try {
304 jmsConnection = jmsConnectionFactory.createConnection();
305 jmsConnection.start();
306 }
307 catch (JMSException e) {
308 throw new ServiceException("JMS Initialize error", e);
309 }
310 }
311
312 public void close() throws ServiceException {
313 try {
314 if (jmsProducer != null)
315 jmsProducer.close();
316 }
317 catch (JMSException e) {
318 log.error(e, "Could not close JMS Producer for channel " + channel.getId());
319 }
320 finally {
321 try {
322 if (jmsProducerSession != null)
323 jmsProducerSession.close();
324 }
325 catch (JMSException e) {
326 log.error(e, "Could not close JMS Producer Session for channel " + channel.getId());
327 }
328 }
329 for (JMSConsumer consumer : consumers.values()) {
330 try {
331 consumer.close();
332 }
333 catch (JMSException e) {
334 log.error(e, "Could not close JMS Consumer " + consumer.subscriptionId + " for channel " + channel.getId());
335 }
336 }
337 try {
338 jmsConnection.stop();
339 }
340 catch (JMSException e) {
341 log.debug(e, "Could not stop JMS Connection for channel " + channel.getId());
342 }
343 finally {
344 try {
345 jmsConnection.close();
346 }
347 catch (JMSException e) {
348 throw new ServiceException("JMS Stop error", e);
349 }
350 finally {
351 consumers.clear();
352 }
353 }
354 }
355
356
357 public void send(AsyncMessage message) throws Exception {
358 Object msg = null;
359 if (Boolean.TRUE.equals(message.getHeader(Gravity.BYTEARRAY_BODY_HEADER))) {
360 byte[] byteArray = (byte[])message.getBody();
361 ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
362 AMF3Deserializer deser = new AMF3Deserializer(bais);
363 msg = deser.readObject();
364 }
365 else
366 msg = message.getBody();
367
368 internalSend(message.getHeaders(), msg, message.getMessageId(), message.getCorrelationId(), message.getTimestamp(), message.getTimeToLive());
369 }
370
371 public void send(Map<String, ?> params, Object msg, long timeToLive) throws Exception {
372 internalSend(params, msg, null, null, new Date().getTime(), timeToLive);
373 }
374
375 public void internalSend(Map<String, ?> headers, Object msg, String messageId, String correlationId, long timestamp, long timeToLive) throws Exception {
376 String topic = (String)headers.get(AsyncMessage.SUBTOPIC_HEADER);
377
378 if (jmsProducerSession == null) {
379 jmsProducerSession = jmsConnection.createSession(transactedSessions, acknowledgeMode);
380 log.debug("Created JMS Producer Session for channel %s (transacted: %s, ack: %s)", channel.getId(), transactedSessions, acknowledgeMode);
381 }
382
383 if (jmsProducer == null) {
384 try {
385 // When failing over, JMS can be in a temporary illegal state. Give it some time to recover.
386 int retryCount = failoverRetryCount;
387 do {
388 try {
389 jmsProducer = jmsProducerSession.createProducer(getProducerDestination(topic));
390 break;
391 }
392 catch (Exception e) {
393 if (retryCount <= 0)
394 throw e;
395
396 if (log.isDebugEnabled())
397 log.debug(e, "Could not create JMS Producer (retrying %d time)", retryCount);
398 else
399 log.info("Could not create JMS Producer (retrying %d time)", retryCount);
400
401 try {
402 Thread.sleep(failoverRetryInterval);
403 }
404 catch (Exception f) {
405 throw new ServiceException("Could not sleep when retrying to create JMS Producer", f.getMessage(), e);
406 }
407 }
408 }
409 while (retryCount-- > 0);
410
411 jmsProducer.setPriority(messagePriority);
412 jmsProducer.setDeliveryMode(deliveryMode);
413 log.debug("Created JMS Producer for channel %s", channel.getId());
414 }
415 catch (JMSException e) {
416 jmsProducerSession.close();
417 jmsProducerSession = null;
418 throw e;
419 }
420 }
421
422 javax.jms.Message jmsMessage = null;
423 if (textMessages)
424 jmsMessage = jmsProducerSession.createTextMessage(msg.toString());
425 else
426 jmsMessage = jmsProducerSession.createObjectMessage((Serializable)msg);
427
428 jmsMessage.setJMSMessageID(normalizeJMSMessageID(messageId));
429 jmsMessage.setJMSCorrelationID(normalizeJMSMessageID(correlationId));
430 jmsMessage.setJMSTimestamp(timestamp);
431 jmsMessage.setJMSExpiration(timeToLive);
432
433 for (Map.Entry<String, ?> me : headers.entrySet()) {
434 if ("JMSType".equals(me.getKey())) {
435 if (me.getValue() instanceof String)
436 jmsMessage.setJMSType((String)me.getValue());
437 }
438 else if ("JMSPriority".equals(me.getKey())) {
439 if (me.getValue() instanceof Integer)
440 jmsMessage.setJMSPriority(((Integer)me.getValue()).intValue());
441 }
442 else if (me.getValue() instanceof String)
443 jmsMessage.setStringProperty(me.getKey(), (String)me.getValue());
444 else if (me.getValue() instanceof Boolean)
445 jmsMessage.setBooleanProperty(me.getKey(), ((Boolean)me.getValue()).booleanValue());
446 else if (me.getValue() instanceof Integer)
447 jmsMessage.setIntProperty(me.getKey(), ((Integer)me.getValue()).intValue());
448 else if (me.getValue() instanceof Long)
449 jmsMessage.setLongProperty(me.getKey(), ((Long)me.getValue()).longValue());
450 else if (me.getValue() instanceof Double)
451 jmsMessage.setDoubleProperty(me.getKey(), ((Double)me.getValue()).doubleValue());
452 else
453 jmsMessage.setObjectProperty(me.getKey(), me.getValue());
454 }
455
456 jmsProducer.send(jmsMessage);
457
458 if (transactedSessions && !useGlassFishNoCommitWorkaround) {
459 // If we are in a container-managed transaction (data dispatch from an EJB interceptor for ex.), we should not commit the session
460 // but the behaviour is different between JBoss and GlassFish
461 try {
462 jmsProducerSession.commit();
463 }
464 catch (JMSException e) {
465 if (e.getMessage() != null && e.getMessage().startsWith("MQJMSRA_DS4001"))
466 useGlassFishNoCommitWorkaround = true;
467 else
468 log.error(e, "Could not commit JMS Session for channel %s", channel.getId());
469 }
470 }
471 }
472
473 private String normalizeJMSMessageID(String messageId) {
474 if (messageId != null && !messageId.startsWith("ID:"))
475 messageId = "ID:" + messageId;
476 return messageId;
477 }
478
479 public void subscribe(CommandMessage message) throws Exception {
480 String subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER);
481 String selector = (String)message.getHeader(CommandMessage.SELECTOR_HEADER);
482 this.topic = (String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER);
483
484 internalSubscribe(subscriptionId, selector, message.getDestination(), this.topic);
485 }
486
487 public void subscribe(String selector, String destination, String topic) throws Exception {
488 if (GraniteContext.getCurrentInstance() instanceof HttpGraniteContext) {
489 String subscriptionId = GraniteDistributedDataFactory.getInstance().getDestinationSubscriptionId(destination);
490 if (subscriptionId != null)
491 internalSubscribe(subscriptionId, selector, destination, topic);
492 }
493 }
494
495 private void internalSubscribe(String subscriptionId, String selector, String destination, String topic) throws Exception {
496 synchronized (consumers) {
497 JMSConsumer consumer = consumers.get(subscriptionId);
498 if (consumer == null) {
499 consumer = new JMSConsumer(subscriptionId, selector, noLocal);
500 consumers.put(subscriptionId, consumer);
501 }
502 else
503 consumer.setSelector(selector);
504 channel.addSubscription(destination, topic, subscriptionId, false);
505 }
506 }
507
508 public void unsubscribe(CommandMessage message) throws Exception {
509 String subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER);
510
511 synchronized (consumers) {
512 JMSConsumer consumer = consumers.get(subscriptionId);
513 try {
514 if (consumer != null)
515 consumer.close();
516 }
517 finally {
518 consumers.remove(subscriptionId);
519 channel.removeSubscription(subscriptionId);
520 }
521 }
522 }
523
524
525 private class JMSConsumer implements MessageListener {
526
527 private String subscriptionId = null;
528 private javax.jms.Session jmsConsumerSession = null;
529 private javax.jms.MessageConsumer jmsConsumer = null;
530 private boolean noLocal = false;
531 private boolean useJBossTCCLDeserializationWorkaround = false;
532 private boolean useGlassFishNoCommitWorkaround = false;
533
534 public JMSConsumer(String subscriptionId, String selector, boolean noLocal) throws Exception {
535 this.subscriptionId = subscriptionId;
536 this.noLocal = noLocal;
537
538 jmsConsumerSession = jmsConnection.createSession(transactedSessions, acknowledgeMode);
539 log.debug("Created JMS Consumer Session for channel %s (transacted: %s, ack: %s)", channel.getId(), transactedSessions, acknowledgeMode);
540
541 try {
542 // When failing over, JMS can be in a temporary illegal state. Give it some time to recover.
543 int retryCount = failoverRetryCount;
544 do {
545 try {
546 jmsConsumer = jmsConsumerSession.createConsumer(getConsumerDestination(topic), selector, noLocal);
547 break;
548 }
549 catch (Exception e) {
550 if (retryCount <= 0)
551 throw e;
552
553 if (log.isDebugEnabled())
554 log.debug(e, "Could not create JMS Consumer (retrying %d time)", retryCount);
555 else
556 log.info("Could not create JMS Consumer (retrying %d time)", retryCount);
557
558 try {
559 Thread.sleep(failoverRetryInterval);
560 }
561 catch (Exception f) {
562 throw new ServiceException("Could not sleep when retrying to create JMS Consumer", f.getMessage(), e);
563 }
564 }
565 }
566 while (retryCount-- > 0);
567
568 jmsConsumer.setMessageListener(this);
569 log.debug("Created JMS Consumer for channel %s", channel.getId());
570 }
571 catch (Exception e) {
572 close();
573 throw e;
574 }
575 }
576
577 public void setSelector(String selector) throws JMSException {
578 if (jmsConsumer != null) {
579 jmsConsumer.close();
580 jmsConsumer = null;
581 }
582 jmsConsumer = jmsConsumerSession.createConsumer(getConsumerDestination(topic), selector, noLocal);
583 jmsConsumer.setMessageListener(this);
584 log.debug("Changed selector to %s for JMS Consumer of channel %s", selector, channel.getId());
585 }
586
587 public void close() throws JMSException {
588 try {
589 if (jmsConsumer != null) {
590 jmsConsumer.close();
591 jmsConsumer = null;
592 }
593 }
594 finally {
595 if (jmsConsumerSession != null) {
596 jmsConsumerSession.close();
597 jmsConsumerSession = null;
598 }
599 }
600 }
601
602 public void onMessage(javax.jms.Message message) {
603 if (!(message instanceof ObjectMessage) && !(message instanceof TextMessage)) {
604 log.error("JMS Adapter message type not allowed: %s", message.getClass().getName());
605
606 try {
607 if (acknowledgeMode == Session.CLIENT_ACKNOWLEDGE)
608 message.acknowledge();
609
610 if (transactedSessions)
611 jmsConsumerSession.commit();
612 }
613 catch (JMSException e) {
614 log.error(e, "Could not ack/commit JMS onMessage");
615 }
616 }
617
618 log.debug("Delivering JMS message to channel %s subscription %s", channel.getId(), subscriptionId);
619
620 AsyncMessage dmsg = new AsyncMessage();
621 try {
622 Serializable msg = null;
623
624 if (textMessages) {
625 TextMessage jmsMessage = (TextMessage)message;
626 msg = jmsMessage.getText();
627 }
628 else {
629 ObjectMessage jmsMessage = (ObjectMessage)message;
630 if (useJBossTCCLDeserializationWorkaround) {
631 // On JBoss 6, try to deserialize with application class loader if the previous attempt fails
632 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
633 try {
634 Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
635 msg = jmsMessage.getObject();
636 }
637 finally {
638 Thread.currentThread().setContextClassLoader(contextClassLoader);
639 }
640 }
641 try {
642 msg = jmsMessage.getObject();
643 }
644 catch (JMSException e) {
645 // On JBoss 6, try to deserialize with application class loader if the previous attempt fails
646 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
647 try {
648 Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
649 msg = jmsMessage.getObject();
650 useJBossTCCLDeserializationWorkaround = true;
651 }
652 finally {
653 Thread.currentThread().setContextClassLoader(contextClassLoader);
654 }
655 }
656 }
657
658 dmsg.setDestination(getDestination().getId());
659
660 if (Boolean.TRUE.equals(message.getBooleanProperty(Gravity.BYTEARRAY_BODY_HEADER))) {
661 getGravity().initThread();
662 try {
663 ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
664 AMF3Serializer ser = new AMF3Serializer(baos, false);
665 ser.writeObject(msg);
666 ser.close();
667 baos.close();
668 dmsg.setBody(baos.toByteArray());
669 }
670 finally {
671 getGravity().releaseThread();
672 }
673 }
674 else
675 dmsg.setBody(msg);
676
677 dmsg.setMessageId(denormalizeJMSMessageID(message.getJMSMessageID()));
678 dmsg.setCorrelationId(denormalizeJMSMessageID(message.getJMSCorrelationID()));
679 dmsg.setTimestamp(message.getJMSTimestamp());
680 dmsg.setTimeToLive(message.getJMSExpiration());
681
682 Enumeration<?> ename = message.getPropertyNames();
683 while (ename.hasMoreElements()) {
684 String pname = (String)ename.nextElement();
685 dmsg.setHeader(pname, message.getObjectProperty(pname));
686 }
687
688 dmsg.setHeader("JMSType", message.getJMSType());
689 dmsg.setHeader("JMSPriority", Integer.valueOf(message.getJMSPriority()));
690 dmsg.setHeader("JMSRedelivered", Boolean.valueOf(message.getJMSRedelivered()));
691 dmsg.setHeader("JMSDeliveryMode", Integer.valueOf(message.getJMSDeliveryMode()));
692 dmsg.setHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER, subscriptionId);
693
694 channel.receive(dmsg);
695 }
696 catch (IOException e) {
697 if (transactedSessions) {
698 try {
699 jmsConsumerSession.rollback();
700 }
701 catch (JMSException f) {
702 log.error("Could not rollback JMS session, messageId: %s", dmsg.getMessageId());
703 }
704 }
705
706 throw new RuntimeException("IO Error", e);
707 }
708 catch (JMSException e) {
709 if (transactedSessions) {
710 try {
711 jmsConsumerSession.rollback();
712 }
713 catch (JMSException f) {
714 log.error("Could not rollback JMS session, messageId: %s", dmsg.getMessageId());
715 }
716 }
717
718 throw new RuntimeException("JMS Error", e);
719 }
720 catch (MessageReceivingException e) {
721 if (transactedSessions) {
722 try {
723 jmsConsumerSession.rollback();
724 }
725 catch (JMSException f) {
726 log.error("Could not rollback JMS session, messageId: %s", dmsg.getMessageId());
727 }
728 }
729
730 throw new RuntimeException("Channel delivery Error", e);
731 }
732
733 try {
734 if (acknowledgeMode == Session.CLIENT_ACKNOWLEDGE)
735 message.acknowledge();
736
737 if (transactedSessions && !useGlassFishNoCommitWorkaround)
738 jmsConsumerSession.commit();
739 }
740 catch (JMSException e) {
741 if (e.getMessage() != null && e.getMessage().startsWith("MQJMSRA_DS4001"))
742 useGlassFishNoCommitWorkaround = true;
743 else
744 log.error(e, "Could not ack/commit JMS onMessage, messageId: %s", dmsg.getMessageId());
745
746 // Message already delivered to client, should rollback or not ?
747 }
748 }
749
750 private String denormalizeJMSMessageID(String messageId) {
751 if (messageId != null && messageId.startsWith("ID:"))
752 messageId = messageId.substring(3);
753 return messageId;
754 }
755 }
756 }
757 }