/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.wso2.carbon.transport.jms.factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.transport.jms.exception.JMSConnectorException;
import org.wso2.carbon.transport.jms.utils.JMSConstants;
import org.wso2.carbon.transport.jms.utils.JMSUtils;
import java.util.Properties;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
/**
* JMSConnectionFactory that handles the JMS Connection, Session creation and closing.
*/
public class JMSConnectionFactory implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory {
private static final Logger logger = LoggerFactory.getLogger(JMSConnectionFactory.class);
/**
* The {@link Context} instance representing initial Context.
*/
private Context ctx;
/**
* The {@link ConnectionFactory} instance representing jms connection factory.
*/
private ConnectionFactory connectionFactory;
/**
* The {@link String} instance representing the connection factory JNDI name.
*/
private String connectionFactoryString;
/**
* Represents whether to listen queue or topic.
*/
private JMSConstants.JMSDestinationType destinationType;
/**
* The {@link Destination} instance representing the jms destination listening to.
*/
private Destination destination;
/**
* The {@link String} instance representing the jms destination name.
*/
private String destinationName;
/**
* The {@link Boolean} instance representing whether the session is transacted or not.
*/
private boolean transactedSession = false;
/**
* The {@link Integer} instance representing the session acknowledgement mode.
*/
private int sessionAckMode = Session.AUTO_ACKNOWLEDGE;
/**
* The {@link String} instance representing the jms spec version.
*/
private String jmsSpec;
/**
* The {@link Boolean} instance representing whether subscription is durable or not.
*/
private boolean isDurable;
/**
* The {@link Boolean} instance representing whether to create a pub-sub connection.
*/
private boolean noPubSubLocal;
/**
* The {@link String} instance representing the client id of the durable subscription.
*/
private String clientId;
/**
* The {@link String} instance representing the subscription name.
*/
private String subscriptionName;
/**
* The {@link String} instance representing the message selector.
*/
private String messageSelector;
/**
* The {@link Boolean} instance representing whether it is a shared subscription or not.
*/
private boolean isSharedSubscription;
/**
* Initialization of JMS ConnectionFactory with the user specified properties.
*
* @param properties Properties to be added to the initial context
* @throws JMSConnectorException Thrown when initial context name is wrong or when creating connection factory.
*/
public JMSConnectionFactory(Properties properties) throws JMSConnectorException {
try {
ctx = new InitialContext(properties);
} catch (NamingException e) {
throw new JMSConnectorException("NamingException while obtaining initial context. ", e);
}
String connectionFactoryType = properties.getProperty(JMSConstants.CONNECTION_FACTORY_TYPE);
if (JMSConstants.DESTINATION_TYPE_TOPIC.equalsIgnoreCase(connectionFactoryType)) {
this.destinationType = JMSConstants.JMSDestinationType.TOPIC;
} else {
this.destinationType = JMSConstants.JMSDestinationType.QUEUE;
}
String jmsSpecVersion = properties.getProperty(JMSConstants.PARAM_JMS_SPEC_VER);
if (null == jmsSpecVersion) {
jmsSpec = JMSConstants.JMS_SPEC_VERSION_1_1;
} else {
switch (jmsSpecVersion) {
case JMSConstants.JMS_SPEC_VERSION_1_1:
jmsSpec = JMSConstants.JMS_SPEC_VERSION_1_1;
break;
case JMSConstants.JMS_SPEC_VERSION_2_0:
jmsSpec = JMSConstants.JMS_SPEC_VERSION_2_0;
break;
case JMSConstants.JMS_SPEC_VERSION_1_0:
jmsSpec = JMSConstants.JMS_SPEC_VERSION_1_0;
break;
default:
jmsSpec = JMSConstants.JMS_SPEC_VERSION_1_1;
}
}
isSharedSubscription = "true"
.equalsIgnoreCase(properties.getProperty(JMSConstants.PARAM_IS_SHARED_SUBSCRIPTION));
noPubSubLocal = Boolean.valueOf(properties.getProperty(JMSConstants.PARAM_PUBSUB_NO_LOCAL));
clientId = properties.getProperty(JMSConstants.PARAM_DURABLE_SUB_CLIENT_ID);
subscriptionName = properties.getProperty(JMSConstants.PARAM_DURABLE_SUB_NAME);
if (isSharedSubscription && subscriptionName == null) {
logger.warn("Subscription name is not given. Therefore declaring a non-shared subscription");
isSharedSubscription = false;
}
String subDurable = properties.getProperty(JMSConstants.PARAM_SUB_DURABLE);
if (null != subDurable) {
isDurable = Boolean.parseBoolean(subDurable);
}
String msgSelector = properties.getProperty(JMSConstants.PARAM_MSG_SELECTOR);
if (null != msgSelector) {
messageSelector = msgSelector;
}
this.connectionFactoryString = properties.getProperty(JMSConstants.CONNECTION_FACTORY_JNDI_NAME);
if (null == connectionFactoryString || "".equals(connectionFactoryString)) {
connectionFactoryString = "QueueConnectionFactory";
}
this.destinationName = properties.getProperty(JMSConstants.DESTINATION_NAME);
String strSessionAck = properties.getProperty(JMSConstants.SESSION_ACK);
if (null == strSessionAck) {
sessionAckMode = Session.AUTO_ACKNOWLEDGE;
} else if (strSessionAck.equals(JMSConstants.CLIENT_ACKNOWLEDGE_MODE)) {
sessionAckMode = Session.CLIENT_ACKNOWLEDGE;
} else if (strSessionAck.equals(JMSConstants.DUPS_OK_ACKNOWLEDGE_MODE)) {
sessionAckMode = Session.DUPS_OK_ACKNOWLEDGE;
} else if (strSessionAck.equals(JMSConstants.SESSION_TRANSACTED_MODE)) {
sessionAckMode = Session.SESSION_TRANSACTED;
transactedSession = true;
}
createConnectionFactory();
}
/**
* To get the JMS Connection Factory.
*
* @return JMS Connection Factory
* @throws JMSConnectorException Thrown when creating jms connection.
*/
public ConnectionFactory getConnectionFactory() throws JMSConnectorException {
if (this.connectionFactory != null) {
return this.connectionFactory;
}
return createConnectionFactory();
}
/**
* To create the JMS Connection Factory.
*
* @return JMS Connection Factory
* @throws JMSConnectorException Thrown when creating {@link ConnectionFactory} instance.
*/
private ConnectionFactory createConnectionFactory() throws JMSConnectorException {
if (null != this.connectionFactory) {
return this.connectionFactory;
}
try {
if (JMSConstants.JMSDestinationType.QUEUE.equals(this.destinationType)) {
this.connectionFactory = (QueueConnectionFactory) ctx.lookup(this.connectionFactoryString);
} else if (JMSConstants.JMSDestinationType.TOPIC.equals(this.destinationType)) {
this.connectionFactory = (TopicConnectionFactory) ctx.lookup(this.connectionFactoryString);
}
} catch (NamingException e) {
throw new JMSConnectorException(
"Naming exception while obtaining connection factory for " + this.connectionFactoryString, e);
}
return this.connectionFactory;
}
/**
* {@inheritDoc}
*/
@Override
public Connection createConnection() throws JMSException {
if (null == connectionFactory) {
throw new JMSException("Connection cannot be establish to the broker. Connection Factory is null. Please "
+ "check the Please check the broker libs provided.");
}
Connection connection = null;
try {
if (JMSConstants.JMS_SPEC_VERSION_1_1.equals(jmsSpec)) {
if (JMSConstants.JMSDestinationType.QUEUE.equals(this.destinationType)) {
connection = ((QueueConnectionFactory) (this.connectionFactory)).createQueueConnection();
} else if (JMSConstants.JMSDestinationType.TOPIC.equals(this.destinationType)) {
connection = ((TopicConnectionFactory) (this.connectionFactory)).createTopicConnection();
if (isDurable) {
connection.setClientID(clientId);
}
}
return connection;
} else {
QueueConnectionFactory qConFac = null;
TopicConnectionFactory tConFac = null;
if (JMSConstants.JMSDestinationType.QUEUE.equals(this.destinationType)) {
qConFac = (QueueConnectionFactory) this.connectionFactory;
} else {
tConFac = (TopicConnectionFactory) this.connectionFactory;
}
if (null != qConFac) {
connection = qConFac.createQueueConnection();
} else {
connection = tConFac.createTopicConnection();
}
if (isDurable && !isSharedSubscription) {
connection.setClientID(clientId);
}
return connection;
}
} catch (JMSException e) {
// Need to close the connection in the case if durable subscriptions
if (null != connection) {
try {
connection.close();
} catch (Exception ex) {
logger.error("Error while closing the connection. ", ex);
}
}
throw e;
}
}
/**
* {@inheritDoc}
*/
@Override
public Connection createConnection(String userName, String password) throws JMSException {
Connection connection = null;
try {
if (JMSConstants.JMS_SPEC_VERSION_1_1.equals(jmsSpec)) {
if (JMSConstants.JMSDestinationType.QUEUE.equals(this.destinationType)) {
connection = ((QueueConnectionFactory) (this.connectionFactory))
.createQueueConnection(userName, password);
} else if (JMSConstants.JMSDestinationType.TOPIC.equals(this.destinationType)) {
connection = ((TopicConnectionFactory) (this.connectionFactory))
.createTopicConnection(userName, password);
if (isDurable) {
connection.setClientID(clientId);
}
}
return connection;
} else {
QueueConnectionFactory qConFac = null;
TopicConnectionFactory tConFac = null;
if (JMSConstants.JMSDestinationType.QUEUE.equals(this.destinationType)) {
qConFac = (QueueConnectionFactory) this.connectionFactory;
} else {
tConFac = (TopicConnectionFactory) this.connectionFactory;
}
if (null != qConFac) {
connection = qConFac.createQueueConnection(userName, password);
} else if (null != tConFac) {
connection = tConFac.createTopicConnection(userName, password);
}
if (isDurable && !isSharedSubscription) {
if (connection == null) {
throw new JMSException(
"Connection is null. Cannot set client ID " + clientId + "for durable subscription");
}
connection.setClientID(clientId);
}
return connection;
}
} catch (JMSException e) {
// Need to close the connection in the case if durable subscriptions
if (null != connection) {
try {
connection.close();
} catch (Exception ex) {
logger.error("Error while closing the connection", ex);
}
}
throw e;
}
}
/**
* {@inheritDoc}
*/
@Override
public JMSContext createContext() {
return connectionFactory.createContext();
}
/**
* {@inheritDoc}
*/
@Override
public JMSContext createContext(int sessionMode) {
return connectionFactory.createContext(sessionMode);
}
/**
* {@inheritDoc}
*/
@Override
public JMSContext createContext(String userName, String password) {
return connectionFactory.createContext(userName, password);
}
/**
* {@inheritDoc}
*/
@Override
public JMSContext createContext(String userName, String password, int sessionMode) {
return connectionFactory.createContext(userName, password, sessionMode);
}
/**
* {@inheritDoc}
*/
@Override
public QueueConnection createQueueConnection() throws JMSException {
return ((QueueConnectionFactory) (this.connectionFactory)).createQueueConnection();
}
/**
* {@inheritDoc}
*/
@Override
public QueueConnection createQueueConnection(String userName, String password) throws JMSException {
return ((QueueConnectionFactory) (this.connectionFactory)).createQueueConnection(userName, password);
}
/**
* {@inheritDoc}
*/
@Override
public TopicConnection createTopicConnection() throws JMSException {
return ((TopicConnectionFactory) (this.connectionFactory)).createTopicConnection();
}
/**
* {@inheritDoc}
*/
@Override
public TopicConnection createTopicConnection(String userName, String password) throws JMSException {
return ((TopicConnectionFactory) (this.connectionFactory)).createTopicConnection(userName, password);
}
/**
* To get the destination of the particular session.
*
* @param session JMS session that we need to find the destination
* @return destination the particular is related with
* @throws JMSConnectorException Thrown when looking up destination
*/
public Destination getDestination(Session session) throws JMSConnectorException {
if (null != this.destination) {
return this.destination;
}
return createDestination(session);
}
/**
* Get a message consumer for particular session and destination.
*
* @param session JMS Session to create the consumer
* @param destination JMS destination which the consumer should listen to
* @return Message Consumer, who is listening in particular destination with the given session
* @throws JMSConnectorException Thrown when creating jms message consumer.
*/
public MessageConsumer getMessageConsumer(Session session, Destination destination) throws JMSConnectorException {
return createMessageConsumer(session, destination);
}
/**
* Create a message consumer for particular session and destination.
*
* @param session JMS Session to create the consumer
* @param destination JMS destination which the consumer should listen to
* @return Message Consumer, who is listening in particular destination with the given session
* @throws JMSConnectorException Thrown when creating jms message consumer
*/
public MessageConsumer createMessageConsumer(Session session, Destination destination)
throws JMSConnectorException {
try {
if (JMSConstants.JMS_SPEC_VERSION_2_0.equals(jmsSpec) && isSharedSubscription) {
if (isDurable) {
return session.createSharedDurableConsumer((Topic) destination, subscriptionName, messageSelector);
} else {
return session.createSharedConsumer((Topic) destination, subscriptionName, messageSelector);
}
} else if ((JMSConstants.JMS_SPEC_VERSION_1_1.equals(jmsSpec)) || (
JMSConstants.JMS_SPEC_VERSION_2_0.equals(jmsSpec) && !isSharedSubscription)) {
if (isDurable) {
return session.createDurableSubscriber((Topic) destination, subscriptionName, messageSelector,
noPubSubLocal);
} else {
return session.createConsumer(destination, messageSelector);
}
} else {
if (JMSConstants.JMSDestinationType.QUEUE.equals(this.destinationType)) {
return ((QueueSession) session).createReceiver((Queue) destination, messageSelector);
} else {
if (isDurable) {
return session
.createDurableSubscriber((Topic) destination, subscriptionName, messageSelector,
noPubSubLocal);
} else {
return ((TopicSession) session).createSubscriber((Topic) destination, messageSelector, false);
}
}
}
} catch (Exception e) {
throw new JMSConnectorException(
"JMS Exception while creating consumer for the destination " + destinationName, e);
}
}
/**
* Get a message producer for particular session and destination.
*
* @param session JMS Session to create the producer
* @param destination JMS destination which the producer should publish to
* @return MessageProducer, who publish messages to particular destination with the given session
* @throws JMSConnectorException Thrown when creating jms message producer
*/
public MessageProducer getMessageProducer(Session session, Destination destination) throws JMSConnectorException {
return createMessageProducer(session, destination);
}
/**
* Create a message producer for particular session and destination.
*
* @param session JMS Session to create the producer
* @param destination JMS destination which the producer should publish to
* @return MessageProducer, who publish messages to particular destination with the given session
* @throws JMSConnectorException Thrown when creating jms message producer
*/
public MessageProducer createMessageProducer(Session session, Destination destination)
throws JMSConnectorException {
try {
if ((JMSConstants.JMS_SPEC_VERSION_1_1.equals(jmsSpec)) || (JMSConstants.JMS_SPEC_VERSION_2_0
.equals(jmsSpec))) {
return session.createProducer(destination);
} else {
if (JMSConstants.JMSDestinationType.QUEUE.equals(this.destinationType)) {
return ((QueueSession) session).createSender((Queue) destination);
} else {
return ((TopicSession) session).createPublisher((Topic) destination);
}
}
} catch (JMSException e) {
throw new JMSConnectorException(
"JMS Exception while creating the producer for the destination " + destinationName, e);
}
}
/**
* To create a destination for particular session.
*
* @param session Specific session to create the destination
* @return destination for particular session
* @throws JMSConnectorException Thrown when looking up destination
*/
private Destination createDestination(Session session) throws JMSConnectorException {
this.destination = createDestination(session, this.destinationName);
return this.destination;
}
/**
* To create the destination.
*
* @param session relevant session to create the destination
* @param destinationName Destination jms destination
* @return the destination that is created from session
* @throws JMSConnectorException Thrown when looking up destination
*/
private Destination createDestination(Session session, String destinationName) throws JMSConnectorException {
Destination destination = null;
try {
if (JMSConstants.JMSDestinationType.QUEUE.equals(this.destinationType)) {
destination = JMSUtils.lookupDestination(ctx, destinationName, JMSConstants.DESTINATION_TYPE_QUEUE);
} else if (JMSConstants.JMSDestinationType.TOPIC.equals(this.destinationType)) {
destination = JMSUtils.lookupDestination(ctx, destinationName, JMSConstants.DESTINATION_TYPE_TOPIC);
}
} catch (NameNotFoundException e) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find destination '" + destinationName + "' on connection factory for '"
+ this.connectionFactoryString + "'. " + e.getMessage());
logger.debug("Creating destination '" + destinationName + "' on connection factory for '"
+ this.connectionFactoryString + ".");
}
/*
If the destination is not found already, create the destination
*/
try {
if (JMSConstants.JMSDestinationType.QUEUE.equals(this.destinationType)) {
destination = session.createQueue(destinationName);
} else if (JMSConstants.JMSDestinationType.TOPIC.equals(this.destinationType)) {
destination = session.createTopic(destinationName);
}
if (logger.isDebugEnabled()) {
logger.debug("Created '" + destinationName + "' on connection factory for '"
+ this.connectionFactoryString + "'.");
}
} catch (JMSException e1) {
throw new JMSConnectorException(
"Could not find nor create '" + destinationName + "' on connection factory for "
+ this.connectionFactoryString, e1);
}
} catch (NamingException e) {
throw new JMSConnectorException(
"Naming exception while looking up for the destination name " + destinationName, e);
}
return destination;
}
/**
* To get a session with the given connection.
*
* @param connection Connection that is needed to create the session
* @return Session that is created from the connection
* @throws JMSConnectorException Thrown when creating jms session
*/
public Session getSession(Connection connection) throws JMSConnectorException {
return createSession(connection);
}
/**
* To create a session from the given connection.
*
* @param connection Specific connection which we is needed for creating session
* @return session created from the given connection
* @throws JMSConnectorException Thrown when creating jms session
*/
public Session createSession(Connection connection) throws JMSConnectorException {
try {
if (JMSConstants.JMS_SPEC_VERSION_1_1.equals(jmsSpec) || JMSConstants.JMS_SPEC_VERSION_2_0
.equals(jmsSpec)) {
return connection.createSession(transactedSession, sessionAckMode);
} else if (JMSConstants.JMSDestinationType.QUEUE.equals(this.destinationType)) {
return ((QueueConnection) (connection))
.createQueueSession(transactedSession, sessionAckMode);
} else {
return ((TopicConnection) (connection))
.createTopicSession(transactedSession, sessionAckMode);
}
} catch (JMSException e) {
throw new JMSConnectorException(
"JMS Exception while obtaining session for factory " + connectionFactoryString, e);
}
}
/**
* Start the jms connection to start the message delivery.
*
* @param connection Connection that need to be started
* @throws JMSConnectorException Thrown when starting jms connection
*/
public void start(Connection connection) throws JMSConnectorException {
try {
connection.start();
} catch (JMSException e) {
throw new JMSConnectorException(
"JMS Exception while starting connection for factory " + this.connectionFactoryString, e);
}
}
/**
* Stop the jms connection to stop the message delivery.
*
* @param connection JMS connection that need to be stopped
* @throws JMSConnectorException Thrown when stopping jms connection
*/
public void stop(Connection connection) throws JMSConnectorException {
try {
if (null != connection) {
connection.stop();
}
} catch (JMSException e) {
throw new JMSConnectorException(
"JMS Exception while stopping the connection for factory " + this.connectionFactoryString, e);
}
}
/**
* Close the jms connection.
*
* @param connection JMS connection that need to be closed
* @throws JMSConnectorException Thrown when closing jms connection
*/
public void closeConnection(Connection connection) throws JMSConnectorException {
try {
if (null != connection) {
connection.close();
}
} catch (JMSException e) {
throw new JMSConnectorException("JMS Exception while closing the connection. ", e);
}
}
/**
* To close the session.
*
* @param session JMS session that need to be closed
* @throws JMSConnectorException Thrown when closing jms session
*/
public void closeSession(Session session) throws JMSConnectorException {
try {
if (null != session) {
session.close();
}
} catch (JMSException e) {
throw new JMSConnectorException("JMS Exception while closing the session. ", e);
}
}
/**
* To close the message consumer.
*
* @param messageConsumer Message consumer that need to be closed
* @throws JMSConnectorException Thrown when closing jms message consumer
*/
public void closeMessageConsumer(MessageConsumer messageConsumer) throws JMSConnectorException {
try {
if (null != messageConsumer) {
messageConsumer.close();
}
} catch (JMSException e) {
throw new JMSConnectorException("JMS Exception while closing the subscriber. ", e);
}
}
/**
* To close the message producer.
*
* @param messageProducer Message producer that need to be closed
* @throws JMSConnectorException Thrown when closing jms message producer
*/
public void closeMessageProducer(MessageProducer messageProducer) throws JMSConnectorException {
try {
if (messageProducer != null) {
messageProducer.close();
}
} catch (JMSException e) {
throw new JMSConnectorException("JMS Exception while closing the producer. ", e);
}
}
}