/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.axis2.transport.jms; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.axis2.context.MessageContext; import org.apache.axis2.transport.base.BaseConstants; import javax.jms.*; import javax.transaction.Transaction; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; /** * Performs the actual sending of a JMS message, and the subsequent committing of a JTA transaction * (if requested) or the local session transaction, if used. An instance of this class is unique * to a single message send out operation and will not be shared. */ public class JMSMessageSender { private static final Log log = LogFactory.getLog(JMSMessageSender.class); /** The Connection to be used to send out */ private Connection connection = null; /** The Session to be used to send out */ private Session session = null; /** The MessageProducer used */ private MessageProducer producer = null; /** Target Destination */ private Destination destination = null; /** The level of cachability for resources */ private int cacheLevel = JMSConstants.CACHE_CONNECTION; /** JMS spec version (1.0.2b or 1.1 or 2.0) default is 1.0.2b */ private String jmsSpecVersion = null; /** Are we sending to a Queue ? */ private Boolean isQueue = null; private Xid jmsXAXid; private XAResource jmsXaResource; private Transaction jmsTransaction; /** * This is a low-end method to support the one-time sends using JMS 1.0.2b * @param connection the JMS Connection * @param session JMS Session * @param producer the MessageProducer * @param destination the JMS Destination * @param cacheLevel cacheLevel - None | Connection | Session | Producer * @param jmsSpecVersion JMS spec version set to (1.0.2b) * @param isQueue posting to a Queue? */ public JMSMessageSender(Connection connection, Session session, MessageProducer producer, Destination destination, int cacheLevel, String jmsSpecVersion, Boolean isQueue) { this.connection = connection; this.session = session; this.producer = producer; this.destination = destination; this.cacheLevel = cacheLevel; this.jmsSpecVersion = jmsSpecVersion; this.isQueue = isQueue; } /** * This is a low-end method to support the one-time sends using JMS 1.0.2b * * @param connection the JMS Connection * @param session JMS Session * @param producer the MessageProducer * @param destination the JMS Destination * @param cacheLevel cacheLevel - None | Connection | Session | Producer * @param jmsSpecVersion JMS spec version set to (1.0.2b) * @param isQueue posting to a Queue? */ public JMSMessageSender(Connection connection, Session session, MessageProducer producer, Destination destination, int cacheLevel, String jmsSpecVersion, Boolean isQueue, Transaction tx, Xid xid, XAResource xaResource) { this.connection = connection; this.session = session; this.producer = producer; this.destination = destination; this.cacheLevel = cacheLevel; this.jmsSpecVersion = jmsSpecVersion; this.isQueue = isQueue; this.jmsTransaction = tx; this.jmsXAXid = xid; this.jmsXaResource = xaResource; } /** * Create a JMSSender using a JMSConnectionFactory and target EPR * * @param jmsConnectionFactory the JMSConnectionFactory * @param targetAddress target EPR */ public JMSMessageSender(JMSConnectionFactory jmsConnectionFactory, String targetAddress) { try { this.cacheLevel = jmsConnectionFactory.getCacheLevel(); this.jmsSpecVersion = jmsConnectionFactory.jmsSpecVersion(); this.connection = jmsConnectionFactory.getConnection(); this.session = jmsConnectionFactory.getSession(connection); boolean isQueue = jmsConnectionFactory.isQueue() == null ? true : jmsConnectionFactory.isQueue(); this.destination = jmsConnectionFactory.getSharedDestination() == null ? jmsConnectionFactory.getDestination(JMSUtils.getDestination(targetAddress), isQueue ? JMSConstants.DESTINATION_TYPE_QUEUE : JMSConstants.DESTINATION_TYPE_TOPIC) : jmsConnectionFactory.getSharedDestination(); this.producer = jmsConnectionFactory.getMessageProducer(connection, session, destination); } catch (Exception e) { handleException("Error while creating message sender", e); } } /** * Perform actual send of JMS message to the Destination selected * * @param message the JMS message * @param msgCtx the Axis2 MessageContext */ public void send(Message message, MessageContext msgCtx) { Boolean jtaCommit = getBooleanProperty(msgCtx, BaseConstants.JTA_COMMIT_AFTER_SEND); Boolean rollbackOnly = getBooleanProperty(msgCtx, BaseConstants.SET_ROLLBACK_ONLY); Boolean persistent = getBooleanProperty(msgCtx, JMSConstants.JMS_DELIVERY_MODE); Integer priority = getIntegerProperty(msgCtx, JMSConstants.JMS_PRIORITY); Integer timeToLive = getIntegerProperty(msgCtx, JMSConstants.JMS_TIME_TO_LIVE); Integer messageDelay = getIntegerProperty(msgCtx, JMSConstants.JMS_MESSAGE_DELAY); // Do not commit, if message is marked for rollback if (rollbackOnly != null && rollbackOnly) { jtaCommit = Boolean.FALSE; } if (persistent != null) { try { producer.setDeliveryMode(DeliveryMode.PERSISTENT); } catch (JMSException e) { handleException("Error setting JMS Producer for PERSISTENT delivery", e); } } if (priority != null) { try { producer.setPriority(priority); } catch (JMSException e) { handleException("Error setting JMS Producer priority to : " + priority, e); } } if (timeToLive != null) { try { producer.setTimeToLive(timeToLive); } catch (JMSException e) { handleException("Error setting JMS Producer TTL to : " + timeToLive, e); } } /** JMS 2.0 feature : Message Delay time interval * This delay will be applied when the message is sent from Broker to consumers * */ if ("2.0".equals(jmsSpecVersion) && messageDelay != null) { try { producer.setDeliveryDelay(messageDelay); } catch (JMSException e) { handleException("Error setting JMS Producer Message Delivery Delay : " + messageDelay, e); } } boolean sendingSuccessful = false; // perform actual message sending try { if ("1.1".equals(jmsSpecVersion) || "2.0".equals(jmsSpecVersion) || isQueue == null) { producer.send(message); } else { if (isQueue) { try { producer.send(message); } catch (JMSException e) { createTempQueueConsumer(); producer.send(message); } } else { try { ((TopicPublisher) producer).publish(message); } catch (JMSException e) { createTempTopicSubscriber(); ((TopicPublisher) producer).publish(message); } } } // set the actual MessageID to the message context for use by any others down the line String msgId = null; try { msgId = message.getJMSMessageID(); if (msgId != null) { msgCtx.setProperty(JMSConstants.JMS_MESSAGE_ID, msgId); } } catch (JMSException ignore) {} sendingSuccessful = true; if (log.isDebugEnabled()) { log.debug("Sent Message Context ID : " + msgCtx.getMessageID() + " with JMS Message ID : " + msgId + " to destination : " + producer.getDestination()); } } catch (JMSException e) { handleException("Error sending message with MessageContext ID : " + msgCtx.getMessageID() + " to destination : " + destination, e); } } /** * Creating a temporary Queue Consumer; The objective of this is to make * a binding for this destination in the message broker. If there is no * bindings created in the server before sending messages, messages will not * be stored in the server. So we create a consumer and close it, if there * are not any bindings already created in the server * * */ public void createTempQueueConsumer() throws JMSException { MessageConsumer consumer = session.createConsumer(destination); consumer.close(); } public void createTempTopicSubscriber() throws JMSException { TopicSubscriber subscriber = ((TopicSession) session).createSubscriber((Topic) destination); subscriber.close(); } /** * Close non-shared producer, session and connection if any */ public void close() { if (producer != null && cacheLevel < JMSConstants.CACHE_PRODUCER) { try { producer.close(); } catch (JMSException e) { log.error("Error closing JMS MessageProducer after send", e); } finally { producer = null; } } if (session != null && cacheLevel < JMSConstants.CACHE_SESSION) { try { session.close(); } catch (JMSException e) { log.error("Error closing JMS Session after send", e); } finally { session = null; } } if (connection != null && cacheLevel < JMSConstants.CACHE_CONNECTION) { try { connection.close(); } catch (JMSException e) { log.error("Error closing JMS Connection after send", e); } finally { connection = null; } } } private void handleException(String message, Exception e) { log.error(message, e); // Cleanup connections on error. See ESBJAVA-4713 if (!isTransacted()) { // else transaction rollback will call closeOnException() due to exception thrown below if (log.isDebugEnabled()) { log.debug("Cleaning up connections on error", e); } closeOnException(); } throw new AxisJMSException(message, e); } /** * Close connection upon exception. See ESBJAVA-4713 */ public void closeOnException() { if (producer != null) { try { producer.close(); } catch (JMSException e) { log.error("Error closing JMS MessageProducer after send", e); } finally { producer = null; } } if (session != null) { try { session.close(); } catch (JMSException e) { log.error("Error closing JMS Session after send", e); } finally { session = null; } } if (connection != null) { try { connection.close(); } catch (JMSException e) { log.error("Error closing JMS Connection after send", e); } finally { connection = null; } } } private boolean isTransacted() { return (jmsXAXid != null || jmsXaResource != null || jmsTransaction != null); } private Boolean getBooleanProperty(MessageContext msgCtx, String name) { Object o = msgCtx.getProperty(name); if (o != null) { if (o instanceof Boolean) { return (Boolean) o; } else if (o instanceof String) { return Boolean.valueOf((String) o); } } return null; } private Integer getIntegerProperty(MessageContext msgCtx, String name) { Object o = msgCtx.getProperty(name); if (o != null) { if (o instanceof Integer) { return (Integer) o; } else if (o instanceof String) { return Integer.parseInt((String) o); } } return null; } public void setConnection(Connection connection) { this.connection = connection; } public void setSession(Session session) { this.session = session; } public void setProducer(MessageProducer producer) { this.producer = producer; } public void setCacheLevel(int cacheLevel) { this.cacheLevel = cacheLevel; } public int getCacheLevel() { return cacheLevel; } public Connection getConnection() { return connection; } public MessageProducer getProducer() { return producer; } public Session getSession() { return session; } public Xid getJmsXAXid() { return jmsXAXid; } public void setJmsXAXid(Xid jmsXAXid) { this.jmsXAXid = jmsXAXid; } public XAResource getJmsXaResource() { return jmsXaResource; } public void setJmsXaResource(XAResource jmsXaResource) { this.jmsXaResource = jmsXaResource; } public Transaction getJmsTransaction() { return jmsTransaction; } public void setJmsTransaction(Transaction jmsTransaction) { this.jmsTransaction = jmsTransaction; } }