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 String topicId = (String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER);
232
233 if (getSecurityPolicy().canPublish(fromClient, topicId, message)) {
234 try {
235 JMSClient jmsClient = connectJMSClient(fromClient, message.getDestination());
236 jmsClient.send(message);
237
238 AsyncMessage reply = new AcknowledgeMessage(message);
239 reply.setMessageId(message.getMessageId());
240
241 return reply;
242 }
243 catch (Exception e) {
244 log.error(e, "Error sending message");
245 ErrorMessage error = new ErrorMessage(message, null);
246 error.setFaultString("JMS Adapter error " + e.getMessage());
247
248 return error;
249 }
250 }
251
252 log.debug("Channel %s tried to publish a message to topic %s", fromClient, topicId);
253 ErrorMessage error = new ErrorMessage(message, null);
254 error.setFaultString("Server.Publish.Denied");
255 return error;
256 }
257
258 @Override
259 public Object manage(Channel fromChannel, CommandMessage message) {
260 String topicId = (String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER);
261
262 if (message.getOperation() == CommandMessage.SUBSCRIBE_OPERATION) {
263 if (getSecurityPolicy().canSubscribe(fromChannel, topicId, message)) {
264 try {
265 JMSClient jmsClient = connectJMSClient(fromChannel, message.getDestination());
266 jmsClient.subscribe(message);
267
268 AsyncMessage reply = new AcknowledgeMessage(message);
269 return reply;
270 }
271 catch (Exception e) {
272 throw new RuntimeException("JMSAdapter subscribe error on topic: " + message, e);
273 }
274 }
275
276 log.debug("Channel %s tried to subscribe to topic %s", fromChannel, topicId);
277 ErrorMessage error = new ErrorMessage(message, null);
278 error.setFaultString("Server.Subscribe.Denied");
279 return error;
280 }
281 else if (message.getOperation() == CommandMessage.UNSUBSCRIBE_OPERATION) {
282 try {
283 JMSClient jmsClient = connectJMSClient(fromChannel, message.getDestination());
284 jmsClient.unsubscribe(message);
285 closeJMSClientIfNecessary(fromChannel, message.getDestination());
286
287 AsyncMessage reply = new AcknowledgeMessage(message);
288 return reply;
289 }
290 catch (Exception e) {
291 throw new RuntimeException("JMSAdapter unsubscribe error on topic: " + message, e);
292 }
293 }
294
295 return null;
296 }
297
298
299 @TransientReference
300 private class JMSClientImpl implements JMSClient {
301
302 private Channel channel = null;
303 private String topic = null;
304 private javax.jms.Connection jmsConnection = null;
305 private javax.jms.Session jmsProducerSession = null;
306 private javax.jms.MessageProducer jmsProducer = null;
307 private Map<String, JMSConsumer> consumers = new HashMap<String, JMSConsumer>();
308 private boolean useGlassFishNoCommitWorkaround = false;
309
310
311 public JMSClientImpl(Channel channel) {
312 this.channel = channel;
313 }
314
315 public boolean hasActiveConsumer() {
316 return consumers != null && !consumers.isEmpty();
317 }
318
319
320 public void connect() throws ServiceException {
321 try {
322 jmsConnection = jmsConnectionFactory.createConnection();
323 jmsConnection.start();
324 }
325 catch (JMSException e) {
326 throw new ServiceException("JMS Initialize error", e);
327 }
328 }
329
330 public void close() throws ServiceException {
331 try {
332 if (jmsProducer != null)
333 jmsProducer.close();
334 }
335 catch (JMSException e) {
336 log.error(e, "Could not close JMS Producer for channel " + channel.getId());
337 }
338 finally {
339 try {
340 if (jmsProducerSession != null)
341 jmsProducerSession.close();
342 }
343 catch (JMSException e) {
344 log.error(e, "Could not close JMS Producer Session for channel " + channel.getId());
345 }
346 }
347 for (JMSConsumer consumer : consumers.values()) {
348 try {
349 consumer.close();
350 }
351 catch (JMSException e) {
352 log.error(e, "Could not close JMS Consumer " + consumer.subscriptionId + " for channel " + channel.getId());
353 }
354 }
355 try {
356 jmsConnection.stop();
357 }
358 catch (JMSException e) {
359 log.debug(e, "Could not stop JMS Connection for channel " + channel.getId());
360 }
361 finally {
362 try {
363 jmsConnection.close();
364 }
365 catch (JMSException e) {
366 throw new ServiceException("JMS Stop error", e);
367 }
368 finally {
369 consumers.clear();
370 }
371 }
372 }
373
374
375 public void send(AsyncMessage message) throws Exception {
376 Object msg = null;
377 if (Boolean.TRUE.equals(message.getHeader(Gravity.BYTEARRAY_BODY_HEADER))) {
378 byte[] byteArray = (byte[])message.getBody();
379 ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
380 AMF3Deserializer deser = new AMF3Deserializer(bais);
381 msg = deser.readObject();
382 }
383 else
384 msg = message.getBody();
385
386 internalSend(message.getHeaders(), msg, message.getMessageId(), message.getCorrelationId(), message.getTimestamp(), message.getTimeToLive());
387 }
388
389 public void send(Map<String, ?> params, Object msg, long timeToLive) throws Exception {
390 internalSend(params, msg, null, null, new Date().getTime(), timeToLive);
391 }
392
393 public void internalSend(Map<String, ?> headers, Object msg, String messageId, String correlationId, long timestamp, long timeToLive) throws Exception {
394 String topic = (String)headers.get(AsyncMessage.SUBTOPIC_HEADER);
395
396 if (jmsProducerSession == null) {
397 jmsProducerSession = jmsConnection.createSession(transactedSessions, acknowledgeMode);
398 log.debug("Created JMS Producer Session for channel %s (transacted: %s, ack: %s)", channel.getId(), transactedSessions, acknowledgeMode);
399 }
400
401 if (jmsProducer == null) {
402 try {
403 // When failing over, JMS can be in a temporary illegal state. Give it some time to recover.
404 int retryCount = failoverRetryCount;
405 do {
406 try {
407 jmsProducer = jmsProducerSession.createProducer(getProducerDestination(topic));
408 break;
409 }
410 catch (Exception e) {
411 if (retryCount <= 0)
412 throw e;
413
414 if (log.isDebugEnabled())
415 log.debug(e, "Could not create JMS Producer (retrying %d time)", retryCount);
416 else
417 log.info("Could not create JMS Producer (retrying %d time)", retryCount);
418
419 try {
420 Thread.sleep(failoverRetryInterval);
421 }
422 catch (Exception f) {
423 throw new ServiceException("Could not sleep when retrying to create JMS Producer", f.getMessage(), e);
424 }
425 }
426 }
427 while (retryCount-- > 0);
428
429 jmsProducer.setPriority(messagePriority);
430 jmsProducer.setDeliveryMode(deliveryMode);
431 log.debug("Created JMS Producer for channel %s", channel.getId());
432 }
433 catch (JMSException e) {
434 jmsProducerSession.close();
435 jmsProducerSession = null;
436 throw e;
437 }
438 }
439
440 javax.jms.Message jmsMessage = null;
441 if (textMessages)
442 jmsMessage = jmsProducerSession.createTextMessage(msg.toString());
443 else
444 jmsMessage = jmsProducerSession.createObjectMessage((Serializable)msg);
445
446 jmsMessage.setJMSMessageID(normalizeJMSMessageID(messageId));
447 jmsMessage.setJMSCorrelationID(normalizeJMSMessageID(correlationId));
448 jmsMessage.setJMSTimestamp(timestamp);
449 jmsMessage.setJMSExpiration(timeToLive);
450
451 for (Map.Entry<String, ?> me : headers.entrySet()) {
452 if ("JMSType".equals(me.getKey())) {
453 if (me.getValue() instanceof String)
454 jmsMessage.setJMSType((String)me.getValue());
455 }
456 else if ("JMSPriority".equals(me.getKey())) {
457 if (me.getValue() instanceof Integer)
458 jmsMessage.setJMSPriority(((Integer)me.getValue()).intValue());
459 }
460 else if (me.getValue() instanceof String)
461 jmsMessage.setStringProperty(me.getKey(), (String)me.getValue());
462 else if (me.getValue() instanceof Boolean)
463 jmsMessage.setBooleanProperty(me.getKey(), ((Boolean)me.getValue()).booleanValue());
464 else if (me.getValue() instanceof Integer)
465 jmsMessage.setIntProperty(me.getKey(), ((Integer)me.getValue()).intValue());
466 else if (me.getValue() instanceof Long)
467 jmsMessage.setLongProperty(me.getKey(), ((Long)me.getValue()).longValue());
468 else if (me.getValue() instanceof Double)
469 jmsMessage.setDoubleProperty(me.getKey(), ((Double)me.getValue()).doubleValue());
470 else
471 jmsMessage.setObjectProperty(me.getKey(), me.getValue());
472 }
473
474 jmsProducer.send(jmsMessage);
475
476 if (transactedSessions && !useGlassFishNoCommitWorkaround) {
477 // If we are in a container-managed transaction (data dispatch from an EJB interceptor for ex.), we should not commit the session
478 // but the behaviour is different between JBoss and GlassFish
479 try {
480 jmsProducerSession.commit();
481 }
482 catch (JMSException e) {
483 if (e.getMessage() != null && e.getMessage().startsWith("MQJMSRA_DS4001"))
484 useGlassFishNoCommitWorkaround = true;
485 else
486 log.error(e, "Could not commit JMS Session for channel %s", channel.getId());
487 }
488 }
489 }
490
491 private String normalizeJMSMessageID(String messageId) {
492 if (messageId != null && !messageId.startsWith("ID:"))
493 messageId = "ID:" + messageId;
494 return messageId;
495 }
496
497 public void subscribe(CommandMessage message) throws Exception {
498 String subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER);
499 String selector = (String)message.getHeader(CommandMessage.SELECTOR_HEADER);
500 this.topic = (String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER);
501
502 internalSubscribe(subscriptionId, selector, message.getDestination(), this.topic);
503 }
504
505 public void subscribe(String selector, String destination, String topic) throws Exception {
506 if (GraniteContext.getCurrentInstance() instanceof HttpGraniteContext) {
507 String subscriptionId = GraniteDistributedDataFactory.getInstance().getDestinationSubscriptionId(destination);
508 if (subscriptionId != null)
509 internalSubscribe(subscriptionId, selector, destination, topic);
510 }
511 }
512
513 private void internalSubscribe(String subscriptionId, String selector, String destination, String topic) throws Exception {
514 synchronized (consumers) {
515 JMSConsumer consumer = consumers.get(subscriptionId);
516 if (consumer == null) {
517 consumer = new JMSConsumer(subscriptionId, selector, noLocal);
518 consumers.put(subscriptionId, consumer);
519 }
520 else
521 consumer.setSelector(selector);
522 channel.addSubscription(destination, topic, subscriptionId, false);
523 }
524 }
525
526 public void unsubscribe(CommandMessage message) throws Exception {
527 String subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER);
528
529 synchronized (consumers) {
530 JMSConsumer consumer = consumers.get(subscriptionId);
531 try {
532 if (consumer != null)
533 consumer.close();
534 }
535 finally {
536 consumers.remove(subscriptionId);
537 channel.removeSubscription(subscriptionId);
538 }
539 }
540 }
541
542
543 private class JMSConsumer implements MessageListener {
544
545 private String subscriptionId = null;
546 private javax.jms.Session jmsConsumerSession = null;
547 private javax.jms.MessageConsumer jmsConsumer = null;
548 private boolean noLocal = false;
549 private boolean useJBossTCCLDeserializationWorkaround = false;
550 private boolean useGlassFishNoCommitWorkaround = false;
551
552 public JMSConsumer(String subscriptionId, String selector, boolean noLocal) throws Exception {
553 this.subscriptionId = subscriptionId;
554 this.noLocal = noLocal;
555
556 jmsConsumerSession = jmsConnection.createSession(transactedSessions, acknowledgeMode);
557 log.debug("Created JMS Consumer Session for channel %s (transacted: %s, ack: %s)", channel.getId(), transactedSessions, acknowledgeMode);
558
559 try {
560 // When failing over, JMS can be in a temporary illegal state. Give it some time to recover.
561 int retryCount = failoverRetryCount;
562 do {
563 try {
564 jmsConsumer = jmsConsumerSession.createConsumer(getConsumerDestination(topic), selector, noLocal);
565 break;
566 }
567 catch (Exception e) {
568 if (retryCount <= 0)
569 throw e;
570
571 if (log.isDebugEnabled())
572 log.debug(e, "Could not create JMS Consumer (retrying %d time)", retryCount);
573 else
574 log.info("Could not create JMS Consumer (retrying %d time)", retryCount);
575
576 try {
577 Thread.sleep(failoverRetryInterval);
578 }
579 catch (Exception f) {
580 throw new ServiceException("Could not sleep when retrying to create JMS Consumer", f.getMessage(), e);
581 }
582 }
583 }
584 while (retryCount-- > 0);
585
586 jmsConsumer.setMessageListener(this);
587 log.debug("Created JMS Consumer for channel %s", channel.getId());
588 }
589 catch (Exception e) {
590 close();
591 throw e;
592 }
593 }
594
595 public void setSelector(String selector) throws JMSException {
596 if (jmsConsumer != null) {
597 jmsConsumer.close();
598 jmsConsumer = null;
599 }
600 jmsConsumer = jmsConsumerSession.createConsumer(getConsumerDestination(topic), selector, noLocal);
601 jmsConsumer.setMessageListener(this);
602 log.debug("Changed selector to %s for JMS Consumer of channel %s", selector, channel.getId());
603 }
604
605 public void close() throws JMSException {
606 try {
607 if (jmsConsumer != null) {
608 jmsConsumer.close();
609 jmsConsumer = null;
610 }
611 }
612 finally {
613 if (jmsConsumerSession != null) {
614 jmsConsumerSession.close();
615 jmsConsumerSession = null;
616 }
617 }
618 }
619
620 public void onMessage(javax.jms.Message message) {
621 if (!(message instanceof ObjectMessage) && !(message instanceof TextMessage)) {
622 log.error("JMS Adapter message type not allowed: %s", message.getClass().getName());
623
624 try {
625 if (acknowledgeMode == Session.CLIENT_ACKNOWLEDGE)
626 message.acknowledge();
627
628 if (transactedSessions)
629 jmsConsumerSession.commit();
630 }
631 catch (JMSException e) {
632 log.error(e, "Could not ack/commit JMS onMessage");
633 }
634 }
635
636 log.debug("Delivering JMS message to channel %s subscription %s", channel.getId(), subscriptionId);
637
638 AsyncMessage dmsg = new AsyncMessage();
639 try {
640 Serializable msg = null;
641
642 if (textMessages) {
643 TextMessage jmsMessage = (TextMessage)message;
644 msg = jmsMessage.getText();
645 }
646 else {
647 ObjectMessage jmsMessage = (ObjectMessage)message;
648 if (useJBossTCCLDeserializationWorkaround) {
649 // On JBoss 6, try to deserialize with application class loader if the previous attempt fails
650 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
651 try {
652 Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
653 msg = jmsMessage.getObject();
654 }
655 finally {
656 Thread.currentThread().setContextClassLoader(contextClassLoader);
657 }
658 }
659 try {
660 msg = jmsMessage.getObject();
661 }
662 catch (JMSException e) {
663 // On JBoss 6, try to deserialize with application class loader if the previous attempt fails
664 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
665 try {
666 Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
667 msg = jmsMessage.getObject();
668 useJBossTCCLDeserializationWorkaround = true;
669 }
670 finally {
671 Thread.currentThread().setContextClassLoader(contextClassLoader);
672 }
673 }
674 }
675
676 dmsg.setDestination(getDestination().getId());
677
678 if (Boolean.TRUE.equals(message.getBooleanProperty(Gravity.BYTEARRAY_BODY_HEADER))) {
679 getGravity().initThread();
680 try {
681 ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
682 AMF3Serializer ser = new AMF3Serializer(baos, false);
683 ser.writeObject(msg);
684 ser.close();
685 baos.close();
686 dmsg.setBody(baos.toByteArray());
687 }
688 finally {
689 getGravity().releaseThread();
690 }
691 }
692 else
693 dmsg.setBody(msg);
694
695 dmsg.setMessageId(denormalizeJMSMessageID(message.getJMSMessageID()));
696 dmsg.setCorrelationId(denormalizeJMSMessageID(message.getJMSCorrelationID()));
697 dmsg.setTimestamp(message.getJMSTimestamp());
698 dmsg.setTimeToLive(message.getJMSExpiration());
699
700 Enumeration<?> ename = message.getPropertyNames();
701 while (ename.hasMoreElements()) {
702 String pname = (String)ename.nextElement();
703 dmsg.setHeader(pname, message.getObjectProperty(pname));
704 }
705
706 dmsg.setHeader("JMSType", message.getJMSType());
707 dmsg.setHeader("JMSPriority", Integer.valueOf(message.getJMSPriority()));
708 dmsg.setHeader("JMSRedelivered", Boolean.valueOf(message.getJMSRedelivered()));
709 dmsg.setHeader("JMSDeliveryMode", Integer.valueOf(message.getJMSDeliveryMode()));
710 dmsg.setHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER, subscriptionId);
711
712 channel.receive(dmsg);
713 }
714 catch (IOException e) {
715 if (transactedSessions) {
716 try {
717 jmsConsumerSession.rollback();
718 }
719 catch (JMSException f) {
720 log.error("Could not rollback JMS session, messageId: %s", dmsg.getMessageId());
721 }
722 }
723
724 throw new RuntimeException("IO Error", e);
725 }
726 catch (JMSException e) {
727 if (transactedSessions) {
728 try {
729 jmsConsumerSession.rollback();
730 }
731 catch (JMSException f) {
732 log.error("Could not rollback JMS session, messageId: %s", dmsg.getMessageId());
733 }
734 }
735
736 throw new RuntimeException("JMS Error", e);
737 }
738 catch (MessageReceivingException e) {
739 if (transactedSessions) {
740 try {
741 jmsConsumerSession.rollback();
742 }
743 catch (JMSException f) {
744 log.error("Could not rollback JMS session, messageId: %s", dmsg.getMessageId());
745 }
746 }
747
748 throw new RuntimeException("Channel delivery Error", e);
749 }
750
751 try {
752 if (acknowledgeMode == Session.CLIENT_ACKNOWLEDGE)
753 message.acknowledge();
754
755 if (transactedSessions && !useGlassFishNoCommitWorkaround)
756 jmsConsumerSession.commit();
757 }
758 catch (JMSException e) {
759 if (e.getMessage() != null && e.getMessage().startsWith("MQJMSRA_DS4001"))
760 useGlassFishNoCommitWorkaround = true;
761 else
762 log.error(e, "Could not ack/commit JMS onMessage, messageId: %s", dmsg.getMessageId());
763
764 // Message already delivered to client, should rollback or not ?
765 }
766 }
767
768 private String denormalizeJMSMessageID(String messageId) {
769 if (messageId != null && messageId.startsWith("ID:"))
770 messageId = messageId.substring(3);
771 return messageId;
772 }
773 }
774 }
775 }