/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* Granite Data Services is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* Granite Data Services is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA, or see <http://www.gnu.org/licenses/>.
*/
package org.granite.gravity.adapters;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.granite.clustering.DistributedDataFactory;
import org.granite.clustering.TransientReference;
import org.granite.config.GraniteConfig;
import org.granite.context.GraniteContext;
import org.granite.gravity.Channel;
import org.granite.gravity.GravityInternal;
import org.granite.gravity.MessageReceivingException;
import org.granite.logging.Logger;
import org.granite.messaging.amf.io.AMF3Deserializer;
import org.granite.messaging.amf.io.AMF3Serializer;
import org.granite.messaging.service.ServiceException;
import org.granite.messaging.webapp.ServletGraniteContext;
import org.granite.util.XMap;
import flex.messaging.messages.AcknowledgeMessage;
import flex.messaging.messages.AsyncMessage;
import flex.messaging.messages.CommandMessage;
import flex.messaging.messages.ErrorMessage;
/**
* @author William DRAI
*/
public class JMSServiceAdapter extends ServiceAdapter {
private static final Logger log = Logger.getLogger(JMSServiceAdapter.class);
public static final long DEFAULT_FAILOVER_RETRY_INTERVAL = 1000L;
public static final long DEFAULT_RECONNECT_RETRY_INTERVAL = 20000L;
public static final int DEFAULT_FAILOVER_RETRY_COUNT = 4;
protected ConnectionFactory jmsConnectionFactory = null;
protected javax.jms.Destination jmsDestination = null;
protected ConcurrentMap<String, JMSClient> jmsClients = new ConcurrentHashMap<String, JMSClient>();
protected String destinationName = null;
protected boolean textMessages = false;
protected boolean transactedSessions = false;
protected int acknowledgeMode = Session.AUTO_ACKNOWLEDGE;
protected int messagePriority = javax.jms.Message.DEFAULT_PRIORITY;
protected int deliveryMode = javax.jms.Message.DEFAULT_DELIVERY_MODE;
protected boolean noLocal = false;
protected boolean sessionSelector = false;
protected long failoverRetryInterval = DEFAULT_FAILOVER_RETRY_INTERVAL;
protected int failoverRetryCount = DEFAULT_FAILOVER_RETRY_COUNT;
protected long reconnectRetryInterval = DEFAULT_RECONNECT_RETRY_INTERVAL;
@Override
public void configure(XMap adapterProperties, XMap destinationProperties) throws ServiceException {
super.configure(adapterProperties, destinationProperties);
log.info("Using JMS configuration: %s", destinationProperties.getOne("jms"));
destinationName = destinationProperties.get("jms/destination-name");
if (Boolean.TRUE.toString().equals(destinationProperties.get("jms/transacted-sessions")))
transactedSessions = true;
String ackMode = destinationProperties.get("jms/acknowledge-mode");
if ("AUTO_ACKNOWLEDGE".equals(ackMode))
acknowledgeMode = Session.AUTO_ACKNOWLEDGE;
else if ("CLIENT_ACKNOWLEDGE".equals(ackMode))
acknowledgeMode = Session.CLIENT_ACKNOWLEDGE;
else if ("DUPS_OK_ACKNOWLEDGE".equals(ackMode))
acknowledgeMode = Session.DUPS_OK_ACKNOWLEDGE;
else if (ackMode != null)
log.warn("Unsupported acknowledge mode: %s (using default AUTO_ACKNOWLEDGE)", ackMode);
if ("javax.jms.TextMessage".equals(destinationProperties.get("jms/message-type")))
textMessages = true;
if (Boolean.TRUE.toString().equals(destinationProperties.get("jms/no-local")))
noLocal = true;
if (Boolean.TRUE.toString().equals(destinationProperties.get("session-selector")))
sessionSelector = true;
failoverRetryInterval = destinationProperties.get("jms/failover-retry-interval", Long.TYPE, DEFAULT_FAILOVER_RETRY_INTERVAL);
if (failoverRetryInterval <= 0) {
log.warn("Illegal failover retry interval: %d (using default %d)", failoverRetryInterval, DEFAULT_FAILOVER_RETRY_INTERVAL);
failoverRetryInterval = DEFAULT_FAILOVER_RETRY_INTERVAL;
}
failoverRetryCount = destinationProperties.get("jms/failover-retry-count", Integer.TYPE, DEFAULT_FAILOVER_RETRY_COUNT);
if (failoverRetryCount <= 0) {
log.warn("Illegal failover retry count: %s (using default %d)", failoverRetryCount, DEFAULT_FAILOVER_RETRY_COUNT);
failoverRetryCount = DEFAULT_FAILOVER_RETRY_COUNT;
}
reconnectRetryInterval = destinationProperties.get("jms/reconnect-retry-interval", Long.TYPE, DEFAULT_RECONNECT_RETRY_INTERVAL);
if (reconnectRetryInterval <= 0) {
log.warn("Illegal reconnect retry interval: %d (using default %d)", reconnectRetryInterval, DEFAULT_RECONNECT_RETRY_INTERVAL);
reconnectRetryInterval = DEFAULT_RECONNECT_RETRY_INTERVAL;
}
Properties environment = new Properties();
for (XMap property : destinationProperties.getAll("jms/initial-context-environment/property")) {
String name = property.get("name");
String value = property.get("value");
if ("Context.PROVIDER_URL".equals(name))
environment.put(Context.PROVIDER_URL, value);
else if ("Context.INITIAL_CONTEXT_FACTORY".equals(name))
environment.put(Context.INITIAL_CONTEXT_FACTORY, value);
else if ("Context.URL_PKG_PREFIXES".equals(name))
environment.put(Context.URL_PKG_PREFIXES, value);
else if ("Context.SECURITY_PRINCIPAL".equals(name))
environment.put(Context.SECURITY_PRINCIPAL, value);
else if ("Context.SECURITY_CREDENTIALS".equals(name))
environment.put(Context.SECURITY_CREDENTIALS, value);
else
log.warn("Unknown InitialContext property: %s (ignored)", name);
}
InitialContext initialContext = null;
try {
initialContext = new InitialContext(environment.size() > 0 ? environment : null);
}
catch (NamingException e) {
log.error(e, "Could not initialize JNDI context");
throw new ServiceException("Error configuring JMS Adapter", e);
}
String cfJndiName = destinationProperties.get("jms/connection-factory");
try {
jmsConnectionFactory = (ConnectionFactory)initialContext.lookup(cfJndiName);
}
catch (NamingException e) {
log.error(e, "Could not find JMS ConnectionFactory named %s in JNDI", cfJndiName);
throw new ServiceException("Error configuring JMS Adapter", e);
}
String dsJndiName = destinationProperties.get("jms/destination-jndi-name");
try {
jmsDestination = (Destination)initialContext.lookup(dsJndiName);
}
catch (NamingException e) {
log.error(e, "Could not find JMS destination named %s in JNDI", dsJndiName);
throw new ServiceException("Error configuring JMS Adapter", e);
}
}
protected javax.jms.Destination getProducerDestination(String topic) {
return jmsDestination;
}
protected javax.jms.Destination getConsumerDestination(String topic) {
return jmsDestination;
}
@Override
public void start() throws ServiceException {
super.start();
}
@Override
public void stop() throws ServiceException {
super.stop();
for (JMSClient jmsClient : jmsClients.values()) {
try {
jmsClient.close();
}
catch (Exception e) {
log.warn(e, "Could not close JMSClient: %s", jmsClient);
}
}
jmsClients.clear();
}
private synchronized JMSClient connectJMSClient(Channel client, String destination) throws Exception {
JMSClient jmsClient = jmsClients.get(client.getId());
if (jmsClient == null) {
jmsClient = new JMSClientImpl(client);
jmsClient.connect();
jmsClients.put(client.getId(), jmsClient);
if (sessionSelector && GraniteContext.getCurrentInstance() instanceof ServletGraniteContext)
((ServletGraniteContext)GraniteContext.getCurrentInstance()).getSessionMap().put(JMSClient.JMSCLIENT_KEY_PREFIX + destination, jmsClient);
log.debug("JMS client connected for channel " + client.getId());
}
return jmsClient;
}
private synchronized void closeJMSClientIfNecessary(Channel channel, String destination) throws Exception {
JMSClient jmsClient = jmsClients.get(channel.getId());
if (jmsClient != null && !jmsClient.hasActiveConsumer()) {
jmsClient.close();
jmsClients.remove(channel.getId());
if (sessionSelector && GraniteContext.getCurrentInstance() instanceof ServletGraniteContext)
((ServletGraniteContext)GraniteContext.getCurrentInstance()).getSessionMap().remove(JMSClient.JMSCLIENT_KEY_PREFIX + destination);
log.debug("JMS client closed for channel " + channel.getId());
}
}
@Override
public Object invoke(Channel fromClient, AsyncMessage message) {
String topicId = (String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER);
if (getSecurityPolicy().canPublish(fromClient, topicId, message)) {
try {
JMSClient jmsClient = connectJMSClient(fromClient, message.getDestination());
jmsClient.send(message);
AsyncMessage reply = new AcknowledgeMessage(message);
reply.setMessageId(message.getMessageId());
return reply;
}
catch (Exception e) {
log.error(e, "Error sending message");
ErrorMessage error = new ErrorMessage(message, null);
error.setFaultString("JMS Adapter error " + e.getMessage());
return error;
}
}
log.debug("Channel %s tried to publish a message to topic %s", fromClient, topicId);
ErrorMessage error = new ErrorMessage(message, null);
error.setFaultString("Server.Publish.Denied");
return error;
}
@Override
public Object manage(Channel fromChannel, CommandMessage message) {
String topicId = (String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER);
if (message.getOperation() == CommandMessage.SUBSCRIBE_OPERATION) {
if (getSecurityPolicy().canSubscribe(fromChannel, topicId, message)) {
try {
JMSClient jmsClient = connectJMSClient(fromChannel, message.getDestination());
jmsClient.subscribe(message);
AsyncMessage reply = new AcknowledgeMessage(message);
return reply;
}
catch (Exception e) {
throw new RuntimeException("JMSAdapter subscribe error on topic: " + message, e);
}
}
log.debug("Channel %s tried to subscribe to topic %s", fromChannel, topicId);
ErrorMessage error = new ErrorMessage(message, null);
error.setFaultString("Server.Subscribe.Denied");
return error;
}
else if (message.getOperation() == CommandMessage.UNSUBSCRIBE_OPERATION) {
try {
JMSClient jmsClient = connectJMSClient(fromChannel, message.getDestination());
jmsClient.unsubscribe(message);
closeJMSClientIfNecessary(fromChannel, message.getDestination());
AsyncMessage reply = new AcknowledgeMessage(message);
return reply;
}
catch (Exception e) {
throw new RuntimeException("JMSAdapter unsubscribe error on topic: " + message, e);
}
}
return null;
}
@TransientReference
private class JMSClientImpl implements JMSClient {
private Channel channel = null;
private String topic = null;
private javax.jms.Connection jmsConnection = null;
private javax.jms.Session jmsSession = null;
private javax.jms.MessageProducer jmsProducer = null;
private Map<String, JMSConsumer> consumers = new HashMap<String, JMSConsumer>();
private boolean useGlassFishNoExceptionListenerWorkaround = false;
private boolean useGlassFishNoCommitWorkaround = false;
private ExceptionListener connectionExceptionListener = new ConnectionExceptionListener();
private class ConnectionExceptionListener implements ExceptionListener {
public void onException(JMSException ex) {
// Connection failure, force reconnection of the producer on next send
jmsProducer = null;
for (JMSConsumer consumer : consumers.values())
consumer.reset();
consumers.clear();
jmsConnection = null;
jmsSession = null;
}
}
public JMSClientImpl(Channel channel) {
this.channel = channel;
}
public boolean hasActiveConsumer() {
return consumers != null && !consumers.isEmpty();
}
public void connect() throws ServiceException {
if (jmsConnection != null)
return;
try {
jmsConnection = jmsConnectionFactory.createConnection();
if (!useGlassFishNoExceptionListenerWorkaround) {
try {
jmsConnection.setExceptionListener(connectionExceptionListener);
}
catch (JMSException e) {
if (e.getMessage().startsWith("MQJMSRA_DC2001: Unsupported:setExceptionListener()"))
useGlassFishNoExceptionListenerWorkaround = true;
else
throw e;
}
}
jmsConnection.start();
log.debug("JMS client connected for channel " + channel.getId());
}
catch (JMSException e) {
throw new ServiceException("JMS Initialize error", e);
}
}
public void close() throws ServiceException {
try {
if (jmsProducer != null)
jmsProducer.close();
}
catch (JMSException e) {
log.error(e, "Could not close JMS Producer for channel " + channel.getId());
}
finally {
for (JMSConsumer consumer : consumers.values()) {
try {
consumer.close();
}
catch (JMSException e) {
log.error(e, "Could not close JMS Consumer " + consumer.subscriptionId + " for channel " + channel.getId());
}
}
try {
if (jmsSession != null)
jmsSession.close();
}
catch (JMSException e) {
log.error(e, "Could not close JMS Session for channel " + channel.getId());
}
}
try {
jmsConnection.stop();
}
catch (JMSException e) {
log.debug(e, "Could not stop JMS Connection for channel " + channel.getId());
}
finally {
try {
jmsConnection.close();
}
catch (JMSException e) {
throw new ServiceException("JMS Stop error", e);
}
finally {
consumers.clear();
}
}
}
private void createProducer(String topic) throws Exception {
try {
// When failing over, JMS can be in a temporary illegal state. Give it some time to recover.
int retryCount = failoverRetryCount;
do {
try {
jmsProducer = jmsSession.createProducer(getProducerDestination(topic != null ? topic : this.topic));
if (retryCount < failoverRetryCount) // We come from a failover, try to recover session
jmsSession.recover();
break;
}
catch (Exception e) {
if (retryCount <= 0)
throw e;
if (log.isDebugEnabled())
log.debug(e, "Could not create JMS Producer (retrying %d time)", retryCount);
else
log.info("Could not create JMS Producer (retrying %d time)", retryCount);
try {
Thread.sleep(failoverRetryInterval);
}
catch (Exception f) {
throw new ServiceException("Could not sleep when retrying to create JMS Producer", f.getMessage(), e);
}
}
}
while (retryCount-- > 0);
jmsProducer.setPriority(messagePriority);
jmsProducer.setDeliveryMode(deliveryMode);
log.debug("Created JMS Producer for channel %s", channel.getId());
}
catch (JMSException e) {
jmsSession.close();
jmsSession = null;
throw e;
}
}
public void send(AsyncMessage message) throws Exception {
Object msg = null;
if (Boolean.TRUE.equals(message.getHeader(GravityInternal.BYTEARRAY_BODY_HEADER))) {
byte[] byteArray = (byte[])message.getBody();
ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
AMF3Deserializer deser = new AMF3Deserializer(bais);
msg = deser.readObject();
deser.close(); // makes jdk7 happy (Resource leak: 'deser' is never closed)...
}
else
msg = message.getBody();
internalSend(message.getHeaders(), msg, message.getMessageId(), message.getCorrelationId(), message.getTimestamp(), message.getTimeToLive());
}
public void send(Map<String, Object> params, Object msg, long timeToLive) throws Exception {
internalSend(params, msg, null, null, new Date().getTime(), timeToLive);
}
public void internalSend(Map<String, Object> headers, Object msg, String messageId, String correlationId, long timestamp, long timeToLive) throws Exception {
String topic = (String)headers.get(AsyncMessage.SUBTOPIC_HEADER);
if (jmsSession == null) {
jmsSession = jmsConnection.createSession(transactedSessions, acknowledgeMode);
log.debug("Created JMS Producer Session for channel %s (transacted: %s, ack: %s)", channel.getId(), transactedSessions, acknowledgeMode);
}
if (jmsProducer == null)
createProducer(topic);
javax.jms.Message jmsMessage = null;
if (textMessages)
jmsMessage = jmsSession.createTextMessage(msg.toString());
else
jmsMessage = jmsSession.createObjectMessage((Serializable)msg);
jmsMessage.setJMSMessageID(normalizeJMSMessageID(messageId));
jmsMessage.setJMSCorrelationID(normalizeJMSMessageID(correlationId));
jmsMessage.setJMSTimestamp(timestamp);
jmsMessage.setJMSExpiration(timeToLive);
for (Map.Entry<String, Object> me : headers.entrySet()) {
if ("JMSType".equals(me.getKey())) {
if (me.getValue() instanceof String)
jmsMessage.setJMSType((String) me.getValue());
}
else if ("JMSPriority".equals(me.getKey())) {
if (me.getValue() instanceof Integer)
jmsMessage.setJMSPriority((Integer) me.getValue());
}
else if (me.getValue() != null)
jmsMessage.setObjectProperty(me.getKey(), me.getValue());
}
jmsProducer.send(jmsMessage);
if (transactedSessions && !useGlassFishNoCommitWorkaround) {
// If we are in a container-managed transaction (data dispatch from an EJB interceptor for ex.), we should not commit the session
// but the behaviour is different between JBoss and GlassFish
try {
jmsSession.commit();
}
catch (JMSException e) {
if (e.getMessage() != null && e.getMessage().startsWith("MQJMSRA_DS4001"))
useGlassFishNoCommitWorkaround = true;
else
log.error(e, "Could not commit JMS Session for channel %s", channel.getId());
}
}
}
private String normalizeJMSMessageID(String messageId) {
if (messageId != null && !messageId.startsWith("ID:"))
messageId = "ID:" + messageId;
return messageId;
}
public void subscribe(CommandMessage message) throws Exception {
String subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER);
String selector = (String)message.getHeader(CommandMessage.SELECTOR_HEADER);
this.topic = (String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER);
internalSubscribe(subscriptionId, selector, message.getDestination(), this.topic);
}
public void subscribe(String selector, String destination, String topic) throws Exception {
GraniteConfig graniteConfig = GraniteContext.getCurrentInstance().getGraniteConfig();
DistributedDataFactory distributedDataFactory = graniteConfig.getDistributedDataFactory();
String subscriptionId = distributedDataFactory.getInstance().getDestinationSubscriptionId(destination);
if (subscriptionId != null)
internalSubscribe(subscriptionId, selector, destination, topic);
}
private void internalSubscribe(String subscriptionId, String selector, String destination, String topic) throws Exception {
synchronized (consumers) {
JMSConsumer consumer = consumers.get(subscriptionId);
if (consumer == null) {
consumer = new JMSConsumer(subscriptionId, selector, noLocal);
consumer.connect(selector);
consumers.put(subscriptionId, consumer);
}
else
consumer.setSelector(selector);
channel.addSubscription(destination, topic, subscriptionId, false);
}
}
public void unsubscribe(CommandMessage message) throws Exception {
String subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER);
synchronized (consumers) {
JMSConsumer consumer = consumers.get(subscriptionId);
try {
if (consumer != null)
consumer.close();
}
finally {
consumers.remove(subscriptionId);
channel.removeSubscription(subscriptionId);
}
}
}
private class JMSConsumer implements MessageListener {
private String subscriptionId = null;
private javax.jms.MessageConsumer jmsConsumer = null;
private boolean noLocal = false;
private String selector = null;
private boolean useJBossTCCLDeserializationWorkaround = false;
private boolean useGlassFishNoCommitWorkaround = false;
private boolean reconnected = false;
private Timer reconnectTimer = null;
public JMSConsumer(String subscriptionId, String selector, boolean noLocal) throws Exception {
this.subscriptionId = subscriptionId;
this.noLocal = noLocal;
this.selector = selector;
}
public void connect(String selector) throws Exception {
if (jmsConsumer != null)
return;
this.selector = selector;
// Reconnect to the JMS provider in case no producer has already done it
JMSClientImpl.this.connect();
if (jmsSession == null) {
jmsSession = jmsConnection.createSession(transactedSessions, acknowledgeMode);
if (reconnected)
jmsSession.recover();
log.debug("Created JMS Consumer Session for channel %s (transacted: %s, ack: %s)", channel.getId(), transactedSessions, acknowledgeMode);
}
if (reconnectTimer != null)
reconnectTimer.cancel();
try {
// When failing over, JMS can be in a temporary illegal state. Give it some time to recover.
int retryCount = failoverRetryCount;
do {
try {
jmsConsumer = jmsSession.createConsumer(getConsumerDestination(topic), selector, noLocal);
if (retryCount < failoverRetryCount) // We come from a failover, try to recover session
reconnected = true;
break;
}
catch (Exception e) {
if (retryCount <= 0)
throw e;
if (log.isDebugEnabled())
log.debug(e, "Could not create JMS Consumer (retrying %d time)", retryCount);
else
log.info("Could not create JMS Consumer (retrying %d time)", retryCount);
try {
Thread.sleep(failoverRetryInterval);
}
catch (Exception f) {
throw new ServiceException("Could not sleep when retrying to create JMS Consumer", f.getMessage(), e);
}
}
}
while (retryCount-- > 0);
jmsConsumer.setMessageListener(this);
log.debug("Created JMS Consumer for channel %s", channel.getId());
}
catch (Exception e) {
close();
throw e;
}
}
public void setSelector(String selector) throws Exception {
// To change the selector, just close the consumer but keep the session
if (jmsConsumer != null) {
jmsConsumer.close();
jmsConsumer = null;
}
connect(selector);
log.debug("Changed selector to %s for JMS Consumer of channel %s", selector, channel.getId());
}
public void reset() {
jmsConsumer = null;
final TimerTask reconnectTask = new TimerTask() {
@Override
public void run() {
try {
connect(selector);
reconnectTimer.cancel();
reconnectTimer = null;
}
catch (Exception e) {
// Wait for next task run
}
}
};
if (reconnectTimer != null)
reconnectTimer.cancel();
reconnectTimer = new Timer();
reconnectTimer.schedule(reconnectTask, failoverRetryInterval, reconnectRetryInterval);
}
public void close() throws JMSException {
if (reconnectTimer != null)
reconnectTimer.cancel();
if (jmsConsumer != null) {
jmsConsumer.close();
jmsConsumer = null;
}
}
public void onMessage(javax.jms.Message message) {
if (!(message instanceof ObjectMessage) && !(message instanceof TextMessage)) {
log.error("JMS Adapter message type not allowed: %s", message.getClass().getName());
try {
if (acknowledgeMode == Session.CLIENT_ACKNOWLEDGE)
message.acknowledge();
if (transactedSessions)
jmsSession.commit();
}
catch (JMSException e) {
log.error(e, "Could not ack/commit JMS onMessage");
}
}
log.debug("Delivering JMS message to channel %s subscription %s", channel.getId(), subscriptionId);
AsyncMessage dmsg = new AsyncMessage();
try {
Serializable msg = null;
if (textMessages) {
TextMessage jmsMessage = (TextMessage)message;
msg = jmsMessage.getText();
}
else {
ObjectMessage jmsMessage = (ObjectMessage)message;
if (useJBossTCCLDeserializationWorkaround) {
// On JBoss 6, try to deserialize with application class loader if the previous attempt fails
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(JMSServiceAdapter.this.getClass().getClassLoader());
msg = jmsMessage.getObject();
}
finally {
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
try {
msg = jmsMessage.getObject();
}
catch (JMSException e) {
// On JBoss 6, try to deserialize with application class loader if the previous attempt fails
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(JMSServiceAdapter.this.getClass().getClassLoader());
msg = jmsMessage.getObject();
useJBossTCCLDeserializationWorkaround = true;
}
finally {
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
}
dmsg.setDestination(getDestination().getId());
if (Boolean.TRUE.equals(message.getBooleanProperty(GravityInternal.BYTEARRAY_BODY_HEADER))) {
getGravity().initThread(null, channel.getClientType());
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
AMF3Serializer ser = new AMF3Serializer(baos);
ser.writeObject(msg);
ser.close();
baos.close();
dmsg.setBody(baos.toByteArray());
}
finally {
getGravity().releaseThread();
}
}
else
dmsg.setBody(msg);
dmsg.setMessageId(denormalizeJMSMessageID(message.getJMSMessageID()));
dmsg.setCorrelationId(denormalizeJMSMessageID(message.getJMSCorrelationID()));
dmsg.setTimestamp(message.getJMSTimestamp());
dmsg.setTimeToLive(message.getJMSExpiration());
Enumeration<?> ename = message.getPropertyNames();
while (ename.hasMoreElements()) {
String pname = (String)ename.nextElement();
dmsg.setHeader(pname, message.getObjectProperty(pname));
}
dmsg.setHeader("JMSType", message.getJMSType());
dmsg.setHeader("JMSPriority", Integer.valueOf(message.getJMSPriority()));
dmsg.setHeader("JMSRedelivered", Boolean.valueOf(message.getJMSRedelivered()));
dmsg.setHeader("JMSDeliveryMode", Integer.valueOf(message.getJMSDeliveryMode()));
dmsg.setHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER, subscriptionId);
channel.receive(dmsg);
}
catch (IOException e) {
if (transactedSessions) {
try {
jmsSession.rollback();
}
catch (JMSException f) {
log.error("Could not rollback JMS session, messageId: %s", dmsg.getMessageId());
}
}
throw new RuntimeException("IO Error", e);
}
catch (JMSException e) {
if (transactedSessions) {
try {
jmsSession.rollback();
}
catch (JMSException f) {
log.error("Could not rollback JMS session, messageId: %s", dmsg.getMessageId());
}
}
throw new RuntimeException("JMS Error", e);
}
catch (MessageReceivingException e) {
if (transactedSessions) {
try {
jmsSession.rollback();
}
catch (JMSException f) {
log.error("Could not rollback JMS session, messageId: %s", dmsg.getMessageId());
}
}
throw new RuntimeException("Channel delivery Error", e);
}
try {
if (acknowledgeMode == Session.CLIENT_ACKNOWLEDGE)
message.acknowledge();
if (transactedSessions && !useGlassFishNoCommitWorkaround)
jmsSession.commit();
}
catch (JMSException e) {
if (e.getMessage() != null && e.getMessage().startsWith("MQJMSRA_DS4001"))
useGlassFishNoCommitWorkaround = true;
else
log.error(e, "Could not ack/commit JMS onMessage, messageId: %s", dmsg.getMessageId());
// Message already delivered to client, should rollback or not ?
}
}
private String denormalizeJMSMessageID(String messageId) {
if (messageId != null && messageId.startsWith("ID:"))
messageId = messageId.substring(3);
return messageId;
}
}
}
}