/*
 * ICESOFT COMMERCIAL SOURCE CODE LICENSE V 1.1
 *
 * The contents of this file are subject to the ICEsoft Commercial Source
 * Code License Agreement V1.1 (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.icesoft.com/license/commercial-source-v1.1.html
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * Copyright 2009-2015 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 */
package com.icesoft.net.messaging.jms;

import com.icesoft.net.messaging.AbstractMessageServiceAdapter;
import com.icesoft.net.messaging.Message;
import com.icesoft.net.messaging.MessageHandler;
import com.icesoft.net.messaging.MessageSelector;
import com.icesoft.net.messaging.MessageServiceAdapter;
import com.icesoft.net.messaging.MessageServiceException;
import com.icesoft.util.ServerUtility;
import org.icepush.Configuration;
import org.icepush.servlet.ServletContextConfiguration;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jms.InvalidDestinationException;
import javax.jms.InvalidSelectorException;
import javax.jms.JMSException;
import javax.jms.JMSSecurityException;
import javax.jms.MessageFormatException;
import javax.jms.Topic;
import javax.jms.TopicConnectionFactory;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;

public class JMSAdapter
extends AbstractMessageServiceAdapter
implements MessageServiceAdapter {
    private static final Logger LOGGER = Logger.getLogger(JMSAdapter.class.getName());

    private static final String ACTIVE_MQ_PROPERTIES = "active-mq.properties";
    private static final String JBOSS_MESSAGING_PROPERTIES = "jboss-messaging.properties";
    private static final String JBOSS_MESSAGING_HA_PROPERTIES = "jboss-messaging-ha.properties";
    private static final String JBOSS7_MESSAGING_PROPERTIES = "jboss7-messaging.properties";
    private static final String OPEN_MQ_PROPERTIES = "open-mq.properties";
    private static final String WEBLOGIC_JMS_PROPERTIES = "weblogic-jms.properties";
    private static final String WEBSPHERE_DEFAULT_MESSAGING = "websphere-default-messaging.properties";

    private Configuration configuration;
    private JMSProviderConfiguration[] jmsProviderConfigurations;
    private ServletContext servletContext;
    private int index = -1;

    private InitialContext initialContext;
    private TopicConnectionFactory topicConnectionFactory;

    public JMSAdapter(final ServletContext servletContext)
    throws IllegalArgumentException {
        super(servletContext);
        this.servletContext = servletContext;
        configuration = new ServletContextConfiguration(this.servletContext);
        String _messagingPropertiesValue = this.servletContext.getInitParameter(MESSAGING_PROPERTIES);
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.log(Level.FINE, "Messaging Properties (web.xml): " + _messagingPropertiesValue);
        }
        if (_messagingPropertiesValue != null) {
            jmsProviderConfigurations = new JMSProviderConfiguration[1];
            try {
                jmsProviderConfigurations[0] =
                    new JMSProviderConfigurationProperties(
                        getClass().getResourceAsStream(_messagingPropertiesValue), configuration);
            } catch (IOException exception) {
                if (LOGGER.isLoggable(Level.WARNING)) {
                    LOGGER.log(
                        Level.WARNING,
                        "An error occurred while reading properties: " + _messagingPropertiesValue,
                        exception);
                }
            }
        } else if (ServerUtility.isGlassFish(this.servletContext)) {
            jmsProviderConfigurations = new JMSProviderConfiguration[1];
            try {
                jmsProviderConfigurations[0] =
                    new JMSProviderConfigurationProperties(
                        getClass().getResourceAsStream(OPEN_MQ_PROPERTIES), configuration);
            } catch (IOException exception) {
                if (LOGGER.isLoggable(Level.WARNING)) {
                    LOGGER.log(
                        Level.WARNING,
                        "An error occurred while reading properties: " + OPEN_MQ_PROPERTIES,
                        exception);
                }
            }
        } else if (ServerUtility.isJBoss7(this.servletContext)) {
            jmsProviderConfigurations = new JMSProviderConfiguration[1];
            try {
                jmsProviderConfigurations[0] =
                    new JMSProviderConfigurationProperties(
                        getClass().getResourceAsStream(JBOSS7_MESSAGING_PROPERTIES), configuration);
            } catch (IOException exception) {
                if (LOGGER.isLoggable(Level.WARNING)) {
                    LOGGER.log(
                        Level.WARNING,
                        "An error occurred while reading properties: " + JBOSS7_MESSAGING_PROPERTIES,
                        exception);
                }
            }
        } else if (ServerUtility.isJBoss(this.servletContext)) {
            jmsProviderConfigurations = new JMSProviderConfiguration[2];
            try {
                jmsProviderConfigurations[0] =
                    new JMSProviderConfigurationProperties(
                        getClass().getResourceAsStream(JBOSS_MESSAGING_PROPERTIES), configuration);
            } catch (IOException exception) {
                if (LOGGER.isLoggable(Level.WARNING)) {
                    LOGGER.log(
                        Level.WARNING,
                        "An error occurred while reading properties: " + JBOSS_MESSAGING_PROPERTIES,
                        exception);
                }
            }
            try {
                jmsProviderConfigurations[1] =
                    new JMSProviderConfigurationProperties(
                        getClass().getResourceAsStream(JBOSS_MESSAGING_HA_PROPERTIES), configuration);
            } catch (IOException exception) {
                if (LOGGER.isLoggable(Level.WARNING)) {
                    LOGGER.log(
                        Level.WARNING,
                        "An error occurred while reading properties: " + JBOSS_MESSAGING_HA_PROPERTIES,
                        exception);
                }
            }
        } else if (ServerUtility.isTomcat(this.servletContext)) {
            jmsProviderConfigurations = new JMSProviderConfigurationProperties[1];
            try {
                jmsProviderConfigurations[0] =
                    new JMSProviderConfigurationProperties(
                        getClass().getResourceAsStream(ACTIVE_MQ_PROPERTIES), configuration);
            } catch (IOException exception) {
                if (LOGGER.isLoggable(Level.WARNING)) {
                    LOGGER.log(
                        Level.WARNING,
                        "An error occurred while reading properties: " + ACTIVE_MQ_PROPERTIES,
                        exception);
                }
            }
        } else if (ServerUtility.isWebLogic(this.servletContext)) {
            jmsProviderConfigurations = new JMSProviderConfigurationProperties[1];
            try {
                jmsProviderConfigurations[0] =
                    new JMSProviderConfigurationProperties(
                        getClass().getResourceAsStream(WEBLOGIC_JMS_PROPERTIES), configuration);
            } catch (IOException exception) {
                if (LOGGER.isLoggable(Level.WARNING)) {
                    LOGGER.log(
                        Level.WARNING,
                        "An error occurred while reading properties: " + WEBLOGIC_JMS_PROPERTIES,
                        exception);
                }
            }
        } else if (ServerUtility.isWebSphere(this.servletContext)) {
            jmsProviderConfigurations = new JMSProviderConfigurationProperties[1];
            try {
                jmsProviderConfigurations[0] =
                    new JMSProviderConfigurationProperties(
                        getClass().getResourceAsStream(WEBSPHERE_DEFAULT_MESSAGING), configuration);
            } catch (IOException exception) {
                if (LOGGER.isLoggable(Level.WARNING)) {
                    LOGGER.log(
                        Level.WARNING,
                        "An error occurred while reading properties: " + WEBSPHERE_DEFAULT_MESSAGING,
                        exception);
                }
            }
        }
        if (jmsProviderConfigurations == null) {
            jmsProviderConfigurations =
                new JMSProviderConfiguration[] {
                    new JMSProviderConfigurationProperties(configuration)
                };
        }
    }

    public void addMessageHandler(final MessageHandler messageHandler, final String topicName) {
        if (messageHandler != null && topicName != null && topicName.trim().length() != 0) {
            synchronized (topicSubscriberMap) {
                if (topicSubscriberMap.containsKey(topicName)) {
                    ((JMSSubscriberConnection)topicSubscriberMap.get(topicName)).addMessageHandler(messageHandler);
                }
            }
        }
    }

    public void close()
    throws MessageServiceException {
        synchronized (topicPublisherMap) {
            synchronized (topicSubscriberMap) {
                if (!topicPublisherMap.isEmpty()) {
                    for (Object _jmsPublisherConnection : topicPublisherMap.values()) {
                        try {
                            // throws JMSException.
                            ((JMSPublisherConnection)_jmsPublisherConnection).close();
                        } catch (JMSException exception) {
                            throw new MessageServiceException(exception);
                        }
                    }
                    topicPublisherMap.clear();
                }
                if (!topicSubscriberMap.isEmpty()) {
                    for (Object _jmsSubscriberConnection : topicSubscriberMap.values()) {
                        try {
                            // throws JMSException.
                            ((JMSSubscriberConnection)_jmsSubscriberConnection).close();
                        } catch (JMSException exception) {
                            throw new MessageServiceException(exception);
                        }
                    }
                    topicSubscriberMap.clear();
                }
                topicConnectionFactory = null;
                if (initialContext != null) {
                    try {
                        initialContext.close();
                    } catch (NamingException exception) {
                        throw new MessageServiceException(exception);
                    } finally {
                        initialContext = null;
                    }
                }
            }
        }
    }

    public JMSProviderConfiguration getJMSProviderConfiguration() {
        return index != -1 ? jmsProviderConfigurations[index] : null;
    }

    public void initialize()
    throws MessageServiceException {
        Properties _environmentProperties = new Properties();
        String _initialContextFactory;
        for (int i = 0; i < jmsProviderConfigurations.length; i++) {
            _initialContextFactory = jmsProviderConfigurations[i].getInitialContextFactory();
            if (_initialContextFactory != null) {
                _environmentProperties.setProperty(
                    JMSProviderConfiguration.INITIAL_CONTEXT_FACTORY, _initialContextFactory);
            }
            String _providerUrl = jmsProviderConfigurations[i].getProviderURL();
            if (_providerUrl != null) {
                _environmentProperties.setProperty(JMSProviderConfiguration.PROVIDER_URL, _providerUrl);
            }
            String _urlPackagePrefixes = jmsProviderConfigurations[i].getURLPackagePrefixes();
            if (_urlPackagePrefixes != null) {
                _environmentProperties.setProperty(JMSProviderConfiguration.URL_PACKAGE_PREFIXES, _urlPackagePrefixes);
            }
            String _securityPrincipal = jmsProviderConfigurations[i].getSecurityPrincipal();
            if (_securityPrincipal != null) {
                _environmentProperties.setProperty(JMSProviderConfiguration.SECURITY_PRINCIPAL, _securityPrincipal);
            }
            String _securityCredentials = jmsProviderConfigurations[i].getSecurityCredentials();
            if (_securityCredentials != null) {
                _environmentProperties.setProperty(JMSProviderConfiguration.SECURITY_CREDENTIALS, _securityCredentials);
            }
            if (LOGGER.isLoggable(Level.FINE)) {
                StringBuffer _environment = new StringBuffer("Trying JMS Environment:\r\n");
                for (Map.Entry _property : _environmentProperties.entrySet()) {
                    _environment.
                        append("        ").
                            append(_property.getKey()).append(" = ").append(_property.getValue()).append("\r\n");
                }
                LOGGER.log(Level.FINE, _environment.toString());
            }
            try {
                // throws NamingException.
                initialContext = new InitialContext(_environmentProperties);
                // throws NamingException.
                topicConnectionFactory =
                    (TopicConnectionFactory)
                        initialContext.lookup(jmsProviderConfigurations[i].getTopicConnectionFactoryName());
                index = i;
                if (LOGGER.isLoggable(Level.FINE)) {
                    StringBuffer _environment = new StringBuffer("Using JMS Environment:\r\n");
                    for (Map.Entry _property : _environmentProperties.entrySet()) {
                        _environment.
                            append("        ").
                                append(_property.getKey()).append(" = ").append(_property.getValue()).append("\r\n");
                    }
                    LOGGER.log(Level.FINE, _environment.toString());
                }
                messageServiceConfiguration = jmsProviderConfigurations[index];
                break;
            } catch (NamingException exception) {
                if (LOGGER.isLoggable(Level.FINE)) {
                    LOGGER.log(Level.FINE, "Failed JMS Environment: " + exception.getMessage());
                }
                if (initialContext != null) {
                    try {
                        initialContext.close();
                    } catch (NamingException e) {
                        // ignoring this one.
                    }
                }
                if ((i + 1) == jmsProviderConfigurations.length) {
                    throw new MessageServiceException(exception);
                }
            }
        }
        if (LOGGER.isLoggable(Level.FINE)) {
            JMSProviderConfiguration _jmsProviderConfiguration = getJMSProviderConfiguration();
            if (_jmsProviderConfiguration != null) {
                LOGGER.log(
                    Level.FINE,
                    new StringBuilder().
                        append("Configuration:\r\n").
                            append(JMSProviderConfiguration.DEFAULT_TOPIC_NAME).append(" = ").
                                append(_jmsProviderConfiguration.getDefaultTopicName()).append("\r\n").
                            append(JMSProviderConfiguration.INITIAL_CONTEXT_FACTORY).append(" = ").
                                append(_jmsProviderConfiguration.getInitialContextFactory()).append("\r\n").
                            append(JMSProviderConfiguration.MESSAGE_MAX_DELAY).append(" = ").
                                append(_jmsProviderConfiguration.getMessageMaxDelay()).append("\r\n").
                            append(JMSProviderConfiguration.MESSAGE_MAX_LENGTH).append(" = ").
                                append(_jmsProviderConfiguration.getMessageMaxLength()).append("\r\n").
                            append(JMSProviderConfiguration.PROVIDER_URL).append(" = ").
                                append(_jmsProviderConfiguration.getProviderURL()).append("\r\n").
                            append(JMSProviderConfiguration.RECEIVE_TIMEOUT).append(" = ").
                                append(_jmsProviderConfiguration.getReceiveTimeout()).append("\r\n").
                            append(JMSProviderConfiguration.SECURITY_PRINCIPAL).append(" = ").
                                append(_jmsProviderConfiguration.getSecurityPrincipal()).append("\r\n").
                            append(JMSProviderConfiguration.THREAD_POOL_SIZE).append(" = ").
                                append(_jmsProviderConfiguration.getThreadPoolSize()).append("\r\n").
                            append(JMSProviderConfiguration.TOPIC_CONNECTION_FACTORY_NAME).
                                append(" = ").append(_jmsProviderConfiguration.getTopicConnectionFactoryName()).append("\r\n").
                            append(JMSProviderConfiguration.TOPIC_NAME_PREFIX).append(" = ").
                                append(_jmsProviderConfiguration.getTopicNamePrefix()).append("\r\n").
                            append(JMSProviderConfiguration.URL_PACKAGE_PREFIXES).append(" = ").
                                append(_jmsProviderConfiguration.getURLPackagePrefixes()).append("\r\n").
                            append(JMSProviderConfiguration.USER_NAME).append(" = ").
                                append(_jmsProviderConfiguration.getUserName()).append("\r\n").
                                    toString());
            }
        }
    }

    public void publish(final Message message, final String topicName)
    throws MessageServiceException {
        if (message != null && topicName != null && topicName.trim().length() != 0) {
            synchronized (topicPublisherMap) {
                JMSPublisherConnection _jmsPublisherConnection;
                if (!topicPublisherMap.containsKey(topicName)) {
                    try {
                        try {
                            // throws NamingException.
                            _jmsPublisherConnection =
                                new JMSPublisherConnection(
                                    lookUpTopic(topicName),
                                    getJMSProviderConfiguration().getUserName(),
                                    getJMSProviderConfiguration().getPassword(),
                                    this);
                        } catch (NamingException exception) {
                            throw new MessageServiceException(exception);
                        }
                        try {
                            // throws JMSException, JMSSecurityException.
                            _jmsPublisherConnection.open();
                            topicPublisherMap.put(topicName, _jmsPublisherConnection);
                        } catch (JMSSecurityException exception) {
                            throw new MessageServiceException(exception);
                        } catch (JMSException exception) {
                            throw new MessageServiceException(exception);
                        }
                    } catch (Exception e) {
                        throw new MessageServiceException(e);
                    }
                } else {
                    _jmsPublisherConnection = (JMSPublisherConnection)topicPublisherMap.get(topicName);
                }
                try {
                    // throws
                    //     InvalidDestinationException, JMSException,
                    //     MessageFormatException.
                    _jmsPublisherConnection.publish(message);
                } catch (InvalidDestinationException exception) {
                    throw new MessageServiceException(exception);
                } catch (MessageFormatException exception) {
                    throw new MessageServiceException(exception);
                } catch (JMSException exception) {
                    throw new MessageServiceException(exception);
                }
            }
        }
    }

    public void removeMessageHandler(final MessageHandler messageHandler, final String topicName) {
        if (messageHandler != null && topicName != null && topicName.trim().length() != 0) {
            synchronized (topicSubscriberMap) {
                if (topicSubscriberMap.containsKey(topicName)) {
                    ((JMSSubscriberConnection)topicSubscriberMap.get(topicName)).removeMessageHandler(messageHandler);
                }
            }
        }
    }

    public void start()
    throws MessageServiceException {
        synchronized (topicSubscriberMap) {
            if (!topicSubscriberMap.isEmpty()) {
                MessageServiceException _messageServiceException = null;
                for (Object _jmsSubscriberConnection : topicSubscriberMap.values()) {
                    try {
                        // throws JMSException.
                        ((JMSSubscriberConnection)_jmsSubscriberConnection).start();
                    } catch (JMSException exception) {
                        if (_messageServiceException == null) {
                            _messageServiceException = new MessageServiceException(exception);
                        }
                    }
                }
                if (_messageServiceException != null) {
                    throw _messageServiceException;
                }
            }
        }
    }

    public void stop()
    throws MessageServiceException {
        synchronized (topicSubscriberMap) {
            if (!topicSubscriberMap.isEmpty()) {
                MessageServiceException _messageServiceException = null;
                for (Object _jmsSubscriberConnection : topicSubscriberMap.values()) {
                    try {
                        // throws JMSException.
                        ((JMSSubscriberConnection)_jmsSubscriberConnection).stop();
                    } catch (JMSException exception) {
                        if (_messageServiceException == null) {
                            _messageServiceException = new MessageServiceException(exception);
                        }
                    }
                }
                if (_messageServiceException != null) {
                    throw _messageServiceException;
                }
            }
        }
    }

    public void subscribe(final String topicName, final MessageSelector messageSelector, final boolean noLocal)
    throws MessageServiceException {
        if (topicName != null && topicName.trim().length() != 0) {
            synchronized (topicSubscriberMap) {
                JMSSubscriberConnection _jmsSubscriberConnection;
                if (!topicSubscriberMap.containsKey(topicName)) {
                    try {
                        // throws NamingException.
                        _jmsSubscriberConnection =
                            new JMSSubscriberConnection(
                                lookUpTopic(topicName),
                                getJMSProviderConfiguration().getUserName(),
                                getJMSProviderConfiguration().getPassword(),
                                this,
                                servletContext);
                    } catch (NamingException exception) {
                        throw new MessageServiceException(exception);
                    }
                    try {
                        // throws JMSException, JMSSecurityException.
                        _jmsSubscriberConnection.open();
                        topicSubscriberMap.put(topicName, _jmsSubscriberConnection);
                    } catch (JMSSecurityException exception) {
                        throw new MessageServiceException(exception);
                    } catch (JMSException exception) {
                        throw new MessageServiceException(exception);
                    }
                } else {
                    _jmsSubscriberConnection = (JMSSubscriberConnection)topicSubscriberMap.get(topicName);
                }
                try {
                    _jmsSubscriberConnection.subscribe(messageSelector, noLocal);
                } catch (InvalidDestinationException exception) {
                    throw new MessageServiceException(exception);
                } catch (InvalidSelectorException exception) {
                    throw new MessageServiceException(exception);
                } catch (JMSException exception) {
                    throw new MessageServiceException(exception);
                }
            }
        }
    }

    public void unsubscribe(final String topicName)
    throws MessageServiceException {
        if (topicName != null && topicName.trim().length() != 0) {
            synchronized (topicSubscriberMap) {
                if (topicSubscriberMap.containsKey(topicName)) {
                    JMSSubscriberConnection _jmsSubscriberConnection =
                        (JMSSubscriberConnection)topicSubscriberMap.remove(topicName);
                    MessageServiceException _messageServiceException = null;
                    try {
                        _jmsSubscriberConnection.stop();
                    } catch (JMSException exception) {
                        // do nothing.
                    }
                    try {
                        _jmsSubscriberConnection.unsubscribe();
                    } catch (JMSException exception) {
                        _messageServiceException = new MessageServiceException(exception);
                    }
                    try {
                        _jmsSubscriberConnection.close();
                    } catch (JMSException exception) {
                        // do nothing.
                    }
                    if (_messageServiceException != null) {
                        throw _messageServiceException;
                    }
                }
            }
        }
    }

    TopicConnectionFactory getTopicConnectionFactory() {
        return topicConnectionFactory;
    }

    private synchronized Topic lookUpTopic(final String topicName)
    throws NamingException {
        String _topicNamePrefix = jmsProviderConfigurations[index].getTopicNamePrefix();
        // throws NamingException.
        return (Topic)initialContext.lookup((_topicNamePrefix == null ? "" : _topicNamePrefix) + topicName);
    }

    /*
     * Taken from "Java Message Service - Version 1.1 April 12, 2002":
     *
     * 2.8 Multithreading
     *
     * JMS could have required that all its objects support concurrent use.  Since support for concurrent access
     * typically adds some overhead and complexity, the JMS design restricts its requirement for concurrent access to
     * those objects that would naturally be shared by a multithreaded client.  The remainder are designed to be
     * accessed by one logical thread of control at a time.
     *
     *     JMS Object        | Supports Concurrent Use
     *     ------------------+------------------------
     *     Destination       | YES
     *     ConnectionFactory | YES
     *     Connection        | YES
     *     Session           | NO
     *     MessageProducer   | NO
     *     MessageConsumer   | NO
     *
     * JMS defines some specific rules that restrict the concurrent use of Sessions.  Since they require more knowledge
     * of JMS specifics than we have presented at this point, they will be described later.  Here we will describe the
     * rationale for imposing them.
     *
     * There are two reasons for restricting concurrent access to Sessions.  First, Sessions are the JMS entity that
     * supports transactions. It is very difficult to implement transactions that are multithreaded.  Second, Sessions
     * support asynchronous message consumption.  It is important that JMS not require that client code used for
     * asynchronous message consumption be capable of handling multiple, concurrent messages.  In addition, if a Session
     * has been set up with multiple, asynchronous consumers, it is important that the client is not forced to handle
     * the case where these separate consumers are concurrently executing.  These restrictions make JMS easier to use
     * for typical clients.  More sophisticated clients can get the concurrency they desire by using multiple sessions.
     */

    /*
     * Taken from "Java 2 Platform Enterprise Edition Specification, v1.3"
     *
     * J2EE.6.7 Java Message Service (JMS) 1.0 Requirements
     *
     * Note that the JMS API creates threads to deliver messages to message listeners.  The use of this message listener
     * facility may be limited by the restrictions on the use of threads in various containers.  In EJB containers, for
     * instance, it is typically not possible to create threads.  The following methods must not be used by application
     * components executing in containers that prevent them from creating threads:
     *
     *     - javax.jms.Session method setMessageListener
     *     - javax.jms.Session method getMessageListener
     *     - javax.jms.Session method run
     *     - javax.jms.QueueConnection method createConnectionConsumer
     *     - javax.jms.TopicConnection method createConnectionConsumer
     *     - javax.jms.TopicConnection method createDurableConnectionConsumer
     *     - javax.jms.MessageConsumer method getMessageListener
     *     - javax.jms.MessageConsumer method setMessageListener
     *
     * In addition, use of the following methods on javax.jms.Connection objects by applications in web and EJB
     * containers may interfere with the connection management functions of the container and must not be used:
     *
     *     - setExceptionListener
     *     - stop
     *     - setClientID
     *
     * A J2EE container may throw a JMSException if the application component violates these restrictions.
     */

    /*
     * Taken from "Java 2 Platform Enterprise Edition Specification, v1.4"
     *
     * J2EE.6.6 Java Message Service (JMS) 1.1 Requirements
     *
     * The following methods may only be used by application components executing in the application client container:
     *
     *     - javax.jms.Session method setMessageListener
     *     - javax.jms.Session method getMessageListener
     *     - javax.jms.Session method run
     *     - javax.jms.QueueConnection method createConnectionConsumer
     *     - javax.jms.TopicConnection method createConnectionConsumer
     *     - javax.jms.TopicConnection method createDurableConnectionConsumer
     *     - javax.jms.MessageConsumer method getMessageListener
     *     - javax.jms.MessageConsumer method setMessageListener
     *     - javax.jms.Connection method setExceptionListener
     *     - javax.jms.Connection method stop
     *     - javax.jms.Connection method setClientID
     *
     * A J2EE container may throw a JMSException (if allowed by the method) if the application component violates these
     * restrictions.
     *
     * Application components in the web and EJB containers must not attempt to create more than one active (not closed)
     * Session object per connection.  An attempt to use the Connection object’s createSession method when an active
     * Session object exists for that connection should be prohibited by the container.  The container may throw a
     * JMSException if the application component violates this restriction.  Application client containers must support
     * the creation of multiple sessions for each connection.
     */
}
