/*
 * 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
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */


package org.objectweb.jonas_jms;

import java.util.StringTokenizer;
import java.util.Vector;

import javax.jms.ConnectionFactory;
import javax.jms.Queue;
import javax.jms.QueueConnectionFactory;
import javax.jms.Topic;
import javax.jms.TopicConnectionFactory;
import javax.jms.XAConnectionFactory;
import javax.jms.XAQueueConnectionFactory;
import javax.jms.XATopicConnectionFactory;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.objectweb.common.Env;
import org.objectweb.jonas_jms.api.JmsAdministration;
import org.objectweb.joram.client.jms.admin.AdminModule;
import org.objectweb.joram.client.jms.admin.User;
import org.objectweb.joram.client.jms.local.XALocalConnectionFactory;
import org.objectweb.joram.client.jms.local.XAQueueLocalConnectionFactory;
import org.objectweb.joram.client.jms.local.XATopicLocalConnectionFactory;
import org.objectweb.joram.client.jms.tcp.QueueTcpConnectionFactory;
import org.objectweb.joram.client.jms.tcp.TcpConnectionFactory;
import org.objectweb.joram.client.jms.tcp.TopicTcpConnectionFactory;
import org.objectweb.joram.client.jms.tcp.XAQueueTcpConnectionFactory;
import org.objectweb.joram.client.jms.tcp.XATcpConnectionFactory;
import org.objectweb.joram.client.jms.tcp.XATopicTcpConnectionFactory;
import org.objectweb.util.monolog.api.BasicLevel;

import fr.dyade.aaa.agent.AgentServer;

/**
 * Joram administration. This JORAM specific class allows to administer a JORAM server
 * when JORAM has been declared as a service.
 *
 * @author Philippe Coq
 * Contributor(s):
 *  Philippe Durieux<p>
 *  Jeff Mesnil: for JORAM 3.x integration<p>
 *  Frederic Maistre for JORAM 4.x integration<p>
 *  03/05/25 Adriana Danes : JMS resource monitoring
 *
 */
public class JmsAdminForJoram implements JmsAdministration {

    private static String QUEUE_CONN_FACT_NAME="JQCF";
    private static String TOPIC_CONN_FACT_NAME="JTCF";
    private static String CONN_FACT_NAME="JCF";
    private boolean collocatedmom = false;
    private InitialContext ictx = null;
    private Vector namelist = new Vector();
    private XAConnectionFactory xacf = null;
    private XATopicConnectionFactory xatcf = null;
    private XAQueueConnectionFactory xaqcf = null;
    private ConnectionFactory jcf = null;
    private TopicConnectionFactory jtcf = null;
    private QueueConnectionFactory jqcf = null;
    private org.objectweb.joram.client.jms.admin.User user = null;
    private org.objectweb.joram.client.jms.admin.User jonasUser = null;
    private String host = null;
    private int port;

    /**
     * default constructor.
     * This class will be created with newInstance().
     */
    public JmsAdminForJoram() {
    }

    // ------------------------------------------------------------
    // private methods
    // ------------------------------------------------------------

    /**
     * start the MOM in the same JVM than the current
     *
     * Start the A3 AgentServer with the default configuration
     * in transient mode and with the a3servers.xml file located into $JONAS_BASE/conf
     * what it is made here is equivalent to
     *  java -Djonas.base=$JONAS_BASE -DTransaction=fr.dyade.aaa.util.NullTransaction
     *       -Dfr.dyade.aaa.agent.A3CONF_DIR=$JONAS_ROOT/config
     *           fr.dyade.aaa.agent.AgentServer 0 ./s0&
     */
    private void startMOM() throws Exception {
        TraceJms.logger.log(BasicLevel.DEBUG,"");

        try {
            // -DTransaction=fr.dyade.aaa.util.NullTransaction
            String TRANSACTION_PROP = "Transaction";
            String s1 = System.getProperty(TRANSACTION_PROP);
            if (s1 == null) {
                System.setProperty(TRANSACTION_PROP, "fr.dyade.aaa.util.NullTransaction");
            }

            // -Dfr.dyade.aaa.agent.A3CONF_DIR=$JONAS_BASE/conf
            String A3CONF_DIR_PROP = "fr.dyade.aaa.agent.A3CONF_DIR";
            String a3config_dir = System.getProperty(A3CONF_DIR_PROP);
            if (a3config_dir == null) {
                System.setProperty(A3CONF_DIR_PROP, System.getProperty("jonas.base")
                                   + System.getProperty("file.separator") + "conf");
            }

            if(TraceJms.isDebug())
                TraceJms.logger.log(BasicLevel.DEBUG,"a3config_dir= "+a3config_dir);
            // java fr.dyade.aaa.agent.AgentServer 0 s0
            // 0 identifies wich server to run among those described in config file.
            // s0 is a directory used for persistence.
            short serverId = 0;
            AgentServer.init(serverId,"s0", TraceJms.getLoggerFactory());
            AgentServer.start();

            // wait 10 millisec for the MOM to start ???
            Thread.sleep(10);

            // Get host and port that must be used to start Admin
            // mainly to get the port number set in a3servers.xml
            short sid = AgentServer.getServerId();	// should be 0
            host = AgentServer.getHostname(sid);	// should be localhost
            port = 16010;
            try {
                String s = AgentServer.getServiceArgs(sid, "org.objectweb.joram.mom.proxies.tcp.TcpProxyService");
                StringTokenizer st = new StringTokenizer(s);
                port = Integer.parseInt(st.nextToken());
            } catch (NumberFormatException exc) {
                TraceJms.logger.log(BasicLevel.ERROR,"exception: "+exc);
            } catch (Exception e) {
                TraceJms.logger.log(BasicLevel.ERROR,"MOM exception:"+e);
                throw e;
            }
            TraceJms.logger.log(BasicLevel.INFO, "starting MOM on host " + host + ", port " + port);

        } catch (Exception ex) {
            TraceJms.logger.log(BasicLevel.ERROR,"exception: "+ex);
            throw ex;
        }
    }

    // ------------------------------------------------------------
    // JmsAdministration implementation
    // ------------------------------------------------------------

    /**
     * Jms Administrator is created with newInstance().
     * initialization is done later with this method.
     * The MOM will be started if collocated.
     * This method should create an XATopicConnectionFactory and
     * an XAQueueConnectionFactory
     * @param boolean true for if the MOM in run in the current JVM
     * @param String url connexion that must be used if not collocated
     */
    public void start(boolean collocated, String url) throws Exception {

        // Start the MOM in this JVM if collocated.
        if (collocated) {
            collocatedmom = true;
            startMOM();
        }

        // Create an InitialContext
        ictx = new InitialContext();

        // Start jms admin
        if (host != null) {
            // collocated: use url got from the MOM
            TraceJms.logger.log(BasicLevel.INFO, "starting JmsAdmin with host " + host + ", port " + port);
            AdminModule.collocatedConnect("root", "root");
        } else if (url != null && url.length() > 0) {
            // not collocated: use url from jonas.properties
            int indexOfHost = url.indexOf("//") + 2;
            int indexOfPort = url.indexOf(":", indexOfHost) + 1;
            host = url.substring(indexOfHost, indexOfPort - 1);
            port = Integer.parseInt(url.substring(indexOfPort));
            TraceJms.logger.log(BasicLevel.WARN, "starting JmsAdmin with host " + host + ", port " + port);
            AdminModule.connect(host, port, "root", "root", 100);
        } else {
            // use default url
            TraceJms.logger.log(BasicLevel.WARN, "starting JmsAdmin with default url");
            AdminModule.connect("root", "root", 100);
        }

        user = User.create("anonymous", "anonymous");
        jonasUser = User.create("jonas", "jonas");

        // Create connection factories  that will be used by the EJB components.
        // An anticipation window is enabled for improving the performance
        if (collocated) {
            xacf = XALocalConnectionFactory.create();
            ((org.objectweb.joram.client.jms.XAConnectionFactory) xacf).getParameters().queueMessageReadMax = 2;
            xatcf = XATopicLocalConnectionFactory.create();
            ((org.objectweb.joram.client.jms.XATopicConnectionFactory) xatcf).getParameters().queueMessageReadMax = 2;
            xaqcf = XAQueueLocalConnectionFactory.create();
            ((org.objectweb.joram.client.jms.XAQueueConnectionFactory) xaqcf).getParameters().queueMessageReadMax = 2;
        }
        else {
            xacf = XATcpConnectionFactory.create(host, port);
            ((org.objectweb.joram.client.jms.XAConnectionFactory) xacf).getParameters().queueMessageReadMax = 2;
            xatcf = XATopicTcpConnectionFactory.create(host, port);
            ((org.objectweb.joram.client.jms.XATopicConnectionFactory) xatcf).getParameters().queueMessageReadMax = 2;
            xaqcf = XAQueueTcpConnectionFactory.create(host, port);
            ((org.objectweb.joram.client.jms.XAQueueConnectionFactory) xaqcf).getParameters().queueMessageReadMax = 2;
        }


        // Create connection factories  that will be used by a pure JMS Client
        String name = CONN_FACT_NAME;
        try {
            jcf = TcpConnectionFactory.create(host, port);
            ictx.rebind(name, jcf);
            namelist.addElement(name);
        } catch (NamingException e) {
            TraceJms.logger.log(BasicLevel.ERROR,"cannot register "+name);
        }

        name = QUEUE_CONN_FACT_NAME;
        try {
            jqcf = QueueTcpConnectionFactory.create(host, port);
            ictx.rebind(name, jqcf);
            namelist.addElement(name);
        } catch (NamingException e) {
            TraceJms.logger.log(BasicLevel.ERROR,"cannot register "+name);
        }

        name = TOPIC_CONN_FACT_NAME;
        try {
            jtcf = TopicTcpConnectionFactory.create(host, port);
            ictx.rebind(name, jtcf);
            namelist.addElement(name);
        } catch (NamingException e) {
            TraceJms.logger.log(BasicLevel.ERROR,"cannot register "+name);
        }
    }

    /**
     * Stop the Jms Administrator
     */
    public void stop() {
        if ( Env.isJAVA4() ){
            // workaround against bug in SUN JVM JDK1.3 on Linux
            try {
                AdminModule.disconnect();
            } catch  (Exception ex) {
                TraceJms.logger.log(BasicLevel.ERROR, "problem for closing admin object " + ex);
            }
            if (collocatedmom) {
                AgentServer.stop();
                if(TraceJms.isDebug())
                    TraceJms.logger.log(BasicLevel.DEBUG, "agentServer stopped");
            }
        }
        // clean up JNDI
        try {
            ictx.unbind(CONN_FACT_NAME);
            ictx.unbind(QUEUE_CONN_FACT_NAME);
            ictx.unbind(TOPIC_CONN_FACT_NAME);
            if(TraceJms.isDebug())
                TraceJms.logger.log(BasicLevel.DEBUG,"unbind connection factories");
        } catch(Exception ex) {
            TraceJms.logger.log(BasicLevel.ERROR,"cannot unbind connection factories");
        }
    }


    /**
     * Get the XAConnectionFactory
     */
    public XAConnectionFactory getXAConnectionFactory() {
        return xacf;
    }

    /**
     * Get the XATopicConnectionFactory
     */
    public XATopicConnectionFactory getXATopicConnectionFactory() {
        return xatcf;
    }

    /**
     * Get the XAQueueConnectionFactory
     */
    public XAQueueConnectionFactory getXAQueueConnectionFactory() {
        return xaqcf;
    }


    /**
     * Create a Queue and bind it to the registry
     */
    public Queue createQueue(String name) throws Exception {
        TraceJms.logger.log(BasicLevel.DEBUG,"");
        org.objectweb.joram.client.jms.Queue queue = null;
        try {
            queue = (org.objectweb.joram.client.jms.Queue) ictx.lookup(name);
        } catch (Exception e) {
            queue = org.objectweb.joram.client.jms.Queue.create(name);
            ictx.rebind(name, queue);
        }
        queue.setWriter(user);
        queue.setReader(user);
        queue.setWriter(jonasUser);
        queue.setReader(jonasUser);
        return queue;
    }

    /**
     * Create a Topic and bind it to the registry
     */
    public Topic createTopic(String name) throws Exception {
        TraceJms.logger.log(BasicLevel.DEBUG,"");
        org.objectweb.joram.client.jms.Topic topic = null;
        try {
            topic = (org.objectweb.joram.client.jms.Topic) ictx.lookup(name);
        } catch (Exception e) {
            topic = org.objectweb.joram.client.jms.Topic.create(name);
            ictx.rebind(name, topic);
        }
        topic.setWriter(user);
        topic.setReader(user);
        topic.setWriter(jonasUser);
        topic.setReader(jonasUser);
        return topic;
    }

    /**
     * Delete a destination.
     */
    public void deleteDestination(String name) throws Exception {
        TraceJms.logger.log(BasicLevel.DEBUG,"");
        try {
            org.objectweb.joram.client.jms.Destination dest = (org.objectweb.joram.client.jms.Destination) ictx.lookup(name);
            dest.delete();
        } catch (Exception e) {
        }
    }

    /**
     * Get number of pending messages on a queue
     * @param queue the monitored queue
     * @exception Exception if could not get info (queue not valid or connection failed or monitoring request failed)
     */
    public int getPendingMessages(javax.jms.Queue queue) throws Exception {
        return ((org.objectweb.joram.client.jms.Queue) queue).getPendingMessages();
    }

    /**
     * Get number of pending requests on a queue
     * @param queue the monitored queue
     * @exception Exception if could not get info (queue not valid or connection failed or monitoring request failed)
     */
    public int getPendingRequests(javax.jms.Queue queue) throws Exception {
        return ((org.objectweb.joram.client.jms.Queue) queue).getPendingRequests();
    }

    /**
     * Get number of subscriptions on a topic
     * @param topic the monitored topic
     * @exception Exception if could not get info (topic not valid or connection failed or monitoring request failed)
     */
    public int getSubscriptions(javax.jms.Topic topic) throws Exception {
        return ((org.objectweb.joram.client.jms.Topic) topic).getSubscriptions();
    }
}
