/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * Contact: jonas-team@objectweb.org
 * 
 * This library 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 any later version.
 * 
 * This library 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * Initial developer(s): Laurent Chauvirey, Frederic Maistre, Nicolas Tachker
 * Contributor(s): 
 * Christophe Ney: for making easier Enhydra integration
 * Philippe Durieux
 * Jeff Mesnil: for JORAM 3.0 integration
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */


package org.objectweb.jonas_jms;

import javax.naming.Context;
import javax.naming.InitialContext; 
import java.util.Properties;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.jms.ConnectionConsumer;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.XAQueueConnectionFactory;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.XATopicConnectionFactory;
import javax.naming.InitialContext; 
import javax.naming.NamingException;
import javax.transaction.TransactionManager;
import org.objectweb.jonas_jms.api.JmsAdministration;
import org.objectweb.jonas_jms.api.JmsManager;

/**
 * JmsManager implementation
 * This singleton class must exist in each JOnAS server that want to use JMS
 */
public class JmsManagerImpl implements JmsManager, JmsJmxManagement {

    private JmsAdministration momadmin = null;
    private InitialContext ictx = null;
    private TopicConnectionFactory tcf = null;
    private QueueConnectionFactory qcf = null;
    private Hashtable queues = new Hashtable();
    private Hashtable topics = new Hashtable();
    private Vector namelist = new Vector();

    private static TransactionManager tm = null;
    private static JmsManagerImpl unique = null;

    /**
     * Private constructor to force only 1 instance.
     */
    private JmsManagerImpl() {
	TraceJms.debug("JmsMananagerImpl private ctor");
    }

    /**
     * Get the JmsManager.
     */
    public static JmsManager getJmsManager() {
	if (unique == null) {
	    unique = new JmsManagerImpl();
	}
	return (JmsManager) unique;
    }

    /**
     * Get the JmsJmxManagement.
     */
    public static JmsJmxManagement getJmsJmxManagement() {
	if (unique == null) {
	    unique = new JmsManagerImpl();
	}
	return (JmsJmxManagement) unique;
    }

    // -------------------------------------------------------------------
    // Internal Methods
    // -------------------------------------------------------------------

    /**
     * return the Transaction Manager
     */
    public static TransactionManager getTransactionManager() {
	return tm;
    }

    // -------------------------------------------------------------------
    // JmsManager  Implementation
    // -------------------------------------------------------------------

   /**
     * Initialization of the JmsManager
     * @param class JmsAdministration class .
     * @param boolean true for launchin the MOM into the EJBServer JVM
     * @param String url connexion that must be used as soon as we don't use
     *              the default value of a3server.xml
     */
    public void init(Class cl, boolean collocated, String url, TransactionManager tm) throws Exception  {
	TraceJms.debug("JmsMananager Init");
	this.tm = tm;
	int maxloops = collocated ? 1 : 5;
	for (int i = 0; i < maxloops; i++) {
	    try {	    
		// Create an InitialContext
		ictx = new InitialContext();
		// Create the MOM instance and start it
		momadmin  = (JmsAdministration) cl.newInstance();
		momadmin.start(collocated, url);
		break;
	    } catch (NamingException e) {
		TraceJms.error("Jmsmanager  cannot get Initial Context"+e);
		throw e;
	    } catch (NullPointerException e) {
		e.printStackTrace();
		throw e;
	    } catch (Exception e) {
		if (i < maxloops) {
		    TraceJms.debug("JmsMananager Init: Cannot reach the MOM - retrying...");
		    try {
			Thread.currentThread().sleep(2000*(i+1));
		    } catch (InterruptedException e2) {
			throw new JMSException("Cannot reach the MOM");
		    }
		} else {
		    TraceJms.error("Jmsmanager  cannot load admin class "+e);
		    throw e;
		}
	    }
	}
	// We must register these factories in JNDI before any client 
	// try to get them.
	getTopicConnectionFactory();
	getQueueConnectionFactory();
    }	

    /**
     * Creation of an administered Object Queue and bind it in the registry
     */
    public Queue createQueue(String name) throws Exception {
	Queue queue  = null;
	// Don't recreate if already found in JNDI
	try {
	    queue = (Queue) ictx.lookup(name);
	    TraceJms.debug("createQueue "+name+" already found");
	    return queue;
	} catch (NamingException ex) {
	}
	TraceJms.debug("JmsManager: Creating and registering queue "+name);
	queue = momadmin.createQueue(name);
	ictx.bind(name, queue);
	namelist.addElement(name);
	queues.put(name, queue);
	return queue;
    }
	
    /**
     * Creation of an administered Object Topic and bind it in the registry
     */
    public Topic createTopic(String name) throws Exception {
	Topic topic = null;
	// Don't recreate if already found in JNDI
	try {
	    topic = (Topic) ictx.lookup(name);
	    TraceJms.debug("createTopic "+name+" already found");
	    return topic;
	} catch (NamingException ex) {
	}
	TraceJms.debug("JmsManager: Creating and registering topic "+name);
	topic = momadmin.createTopic(name);
	ictx.bind(name, topic);
	namelist.addElement(name);
	topics.put(name, topic);
	return topic;
    }

    /**
     *  Get the unique TopicConnectionFactory
     */
    public TopicConnectionFactory getTopicConnectionFactory() {
	if (tcf == null) {
	    String name = "TCF";
	    tcf = new org.objectweb.jonas_jms.JTopicConnectionFactory(name);
	    try {
		TraceJms.debug("JmsManager: Creating and registering "+name);
		ictx.rebind(name, tcf);
	    } catch (NamingException e) {
		TraceJms.debug("JmsManager: cannot rebind "+name+" :"+e);
	    }
	}
	return tcf;
    }
 
    /*
     *  Get the unique QueueConnectionFactory
     */
    public QueueConnectionFactory getQueueConnectionFactory() {
	if (qcf == null) {
	    String name = "QCF";
	    qcf = new org.objectweb.jonas_jms.JQueueConnectionFactory(name);
	    try {
		TraceJms.debug("JmsManager: Creating and registering "+name);
		ictx.rebind(name, qcf);
	    } catch (NamingException e) {
		TraceJms.debug("JmsManager: cannot rebind "+name+" :"+e);
	    }
	}
	return qcf;
    }

    /**
     *  Get Default XATopicConnectionFactory
     */
    public XATopicConnectionFactory getXATopicConnectionFactory() {
	return momadmin.getXATopicConnectionFactory();
    }
 
    /**
     *  Get Default XAQueueConnectionFactory
     */
    public XAQueueConnectionFactory getXAQueueConnectionFactory() {
	return momadmin.getXAQueueConnectionFactory();
    }

    /**
     *  Get Queue (creates it if not exist)
     */
    public Queue getQueue(String name) throws Exception {
	Queue q = (Queue)queues.get(name);
	if (q == null) {
	    q = createQueue(name);
	}
	return q;
    }

    /**
     *  Get Topic (creates it if not exist)
     */
    public Topic getTopic(String name) throws Exception {
	Topic t = (Topic)topics.get(name);
	if (t == null) {
	    t = createTopic(name);
	}
	return t;
    }

    /**
     *  Get Topic Names
     */
    public Enumeration getTopicsNames() {
	return topics.keys();
    }   

    /**
     *  Get Queue Names
     */
    public Enumeration getQueuesNames() {
	return queues.keys();
    }

    /**
     * Terminate the administering process
     */
    public void stop() throws Exception {
	// Stop the MOM
	if (momadmin != null) {
	    momadmin.stop();
	}
	// clean up JNDI
	Enumeration ln = namelist.elements();
	while (ln.hasMoreElements()) {
	    String name = (String)ln.nextElement();
	    try {		    
		ictx.unbind(name);
		TraceJms.debug("JmsManager : unbind "+name);
	    } catch(NamingException e) {
		TraceJms.debug("JmsManager : cannot unbind "+name);
	    }
	}
    }

    // -------------------------------------------------------------
    // Management methods
    // -------------------------------------------------------------

    /**
     * @return the current number of Topic Jms Connection Factory
     */ 
    public int getCurrentNumberOfJmsTopicConnectionFactory() {
	int result = 0;
	if (tcf != null) result++;
	return result;
    }

    /**
     * @return the current number of Queue Jms Connection Factory
     */ 
    public int getCurrentNumberOfJmsQueueConnectionFactory() {
	int result = 0;
	if (qcf != null) result++;
	return result;
    }

    /**
     * @return the current number of Topic Jms Destination
     */ 
    public int getCurrentNumberOfJmsTopicDestination() {
	return topics.size();
    }

    /**
     * @return the current number of Queue Jms Destination
     */ 
    public int getCurrentNumberOfJmsQueueDestination() {
	return queues.size();
    }

    /**
     * Remove a Jms destination
     * @param String jndi name
     */
    public void removeJmsDestination(String jndiName) throws Exception {
	if ((queues.containsKey(jndiName))||(topics.containsKey(jndiName))) {
	    try {	    
		InitialContext ictx = new InitialContext();
		ictx.unbind(jndiName);
		queues.remove(jndiName);
		topics.remove(jndiName);
	    } catch(Exception ex) {
		throw new Exception("JmsManagerImpl remove destination : cannot unbind "+jndiName );
	    }
	} else {
	    throw new Exception("Unexisting jms destination :" + jndiName +" remove abord");
	}
    }
    
    /**
     * @return String name of default Queue Connection factory
     */
    public String getDefaultQueueConnectionFactoryName() {
	return "QCF"; 
    }

    /**
     * @return String name of default Topic Connection factory
     */
    public String getDefaultTopicConnectionFactoryName() {
	return "TCF"; 
    }
}
