package org.apache.soap.transport.jms;

import javax.jms.*;
import javax.naming.*;
import java.util.*;
import java.net.*;


/**
 * Some utilities to deal with JMS.
 *
 * As well, it can be instantiate to keep all the JMS and jndi objects around.
 */
public class JMSUtil {
    private Queue           _queue;
    private QueueConnection _queueConnection;
    private QueueReceiver   _queueReceiver;
    private QueueSender    _queueSender;
    private QueueSession    _queueSession;
    private Context         _jndiContext;
    private String _jndiFactory;
    private String _jndiURL;
    private String _queueName;

    /**
     * JNDI and JMS objects are looked up and created lazily.
     * @param jndiFactory JNDI INITIAL_CONTEXT_FACTORY
     * @param jndiURL The JDNI PROVIDER_URL.
     * @param queueName The name of the queue to be operated on. It is assumed
     *      that a user connects only to one queue, and is optmized for that.
     *      if you want to connect to a second queue, use the createJMSUtil(queue)
     *      method, as it will save some network work.
     */
    public JMSUtil(String jndiFactory, String jndiURL, String queueName) {
        _jndiFactory = jndiFactory;
        _jndiURL     = jndiURL;
        _queueName   = queueName;
    }

    /**
     * Same as other constructor, but it passes the Queue, initial JNDI context
     * in, so that they don't have to be recreated. This is to be used through
     * the createJMSUtil method.
     */
    public JMSUtil(String jndiFactory, String jndiURL, String queueName,
                   Context jndiContext) {
        _jndiFactory = jndiFactory;
        _jndiURL     = jndiURL;
        _queueName   = queueName;
        _jndiContext = jndiContext;

        System.out.println("cloned JMSUtil " + queueName);
    }
    /**
     * Get jndi Context
     */
    public Context getContext() throws NamingException {
        if (_jndiContext == null) {
            _jndiContext = getInitialContext(_jndiFactory, _jndiURL);
        }
        // dummy check
        if (_jndiContext == null) {
            System.err.println("JMSUtil.getContext() failed to lookup Context");
            System.err.println("INITIAL_CONTEXT_FACTORY = " + _jndiFactory);
            System.err.println("PROIVDER_RUL = " + _jndiURL);
        }

        return _jndiContext;
    }

    /**
     * Get the JMS QueueConnection
     */
    public QueueConnection getQueueConnection() throws NamingException
                                                       , JMSException {
        if (_queueConnection == null) {
            _queueConnection = lookupQueueConnection(getContext());
        }

        return _queueConnection;
    }

    /**
     * Get JMS Queue
     */
    public Queue getQueue() throws NamingException {
        if (_queue == null) {
            _queue = lookupQueue(_queueName, _jndiContext);
        }

        return _queue;
    }

    /**
     * Get QueueReceiver and start connection, if it is not already started.
     */
    public QueueReceiver getQueueReceiver() throws NamingException
                                                   , JMSException {
        if (_queueReceiver == null) {
            _queueReceiver = getQueueSession().createReceiver(getQueue());
            getQueueConnection().start();
        }

        return _queueReceiver;
    }

    /**
     * Gets the Queue session with session set to AUTO_ACKNOWLEDGE
     */
    public QueueSession getQueueSession() throws NamingException
                                            , JMSException{
        if (_queueSession == null) {
            _queueSession = getQueueConnection().createQueueSession(false,
                                                 Session.AUTO_ACKNOWLEDGE);
        }

        return _queueSession;
    }

    /**
     * Get QueueSender and start connection, if it is not already started.
     */
    public QueueSender getQueueSender() throws NamingException, JMSException {
        if (_queueSender == null) {
            QueueSession session = getQueueConnection().createQueueSession(
                false, Session.AUTO_ACKNOWLEDGE);
            _queueSender = session.createSender(getQueue());
            getQueueConnection().start();
        }

        return _queueSender;
    }

    /**
     * Creates another JMSUtil to a new Queue, while re-using the jndi
     * initial Context lookup and QueueFactory.
     */
    public JMSUtil createJMSUtil(String queueName) {
        return new JMSUtil(_jndiFactory,
                           _jndiURL,
                           queueName,
                           _jndiContext);
    }

    /**
     * Does a jndi lookup.
     * @param jndiFactory Specify the INITIAL_CONTEXT_FACTORY class
     * @param providerUrl  Sepcifiy the PROVIDER_URL
     * @return obj It returns, whatever jndi comes upon ;-)
     */
    public static Context lookupContext(String jndiFactory
                                    , String providerUrl) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, jndiFactory);
        env.put(Context.PROVIDER_URL, providerUrl);
        return new InitialContext(env);
    }

    /**
     * Creates an InitialContext for JNDI.
     * @param jndiFactory Specify the INITIAL_CONTEXT_FACTORY class
     * @param providerUrl  Sepcifiy the PROVIDER_URL
     * @return obj It returns, whatever jndi comes upon ;-)
     */
    public static InitialContext getInitialContext(String jndiFactory
                                    , String providerUrl) throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, jndiFactory);
        env.put(Context.PROVIDER_URL, providerUrl);
        return new InitialContext(env);
    }

    /**
     * Looks up a Queue in jndi
     * @param name The jndi name for the queue.
     * @param context The jndi context to use.
     */
    public static Queue lookupQueue(String name, Context context) throws NamingException {
        return (Queue) context.lookup(name);
    }

    /**
     * looks up and creates a QueueConnection.
     * @param context The jndi context to use for the lookup.
     */
    public static QueueConnection lookupQueueConnection(Context context)
                                                        throws NamingException
                                                               , JMSException {
        if (context == null) {
            System.err.println("JMSUtil.lookupQueueConnection was called with"
                               + " null as Context.");
            throw new NullPointerException("JMSUtil.lookupQueueConnection doesn't like null as parameter");
        }
        QueueConnectionFactory factory = (QueueConnectionFactory)
            context.lookup("QueueConnectionFactory");
        return factory.createQueueConnection();

    }

}