/**
 * 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.jmx;

import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.rmi.PortableRemoteObject;

// JOnAS Log
import org.objectweb.jonas.common.Log;

// Monolog
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.BasicLevel;
/**
 * Manages the current "jonas provided" RMIConnector used to communicate with the MBEanServer.
 * @author Bruno Michel and Guillaume Riviere
 */
public class ConnectorFactory {

    private static String currentRMIConnectorName = null;
    private static RMIConnector rmic = null; // current RMIConnector
    private static Context context = null;
    private static Logger logger = Log.getLogger("objectweb.org.jonas.jmx");

    /**
     * @return Context the current application context, create an initial context
     * if there is no current context.
     */
    public static Context getContext() throws javax.naming.NamingException {
        if (context == null) {
            context = new InitialContext();
        }
        return context;
    }

    /**
     * @return String the name of the current RMI connector.
     * If no RMI connector is selected, returns the first RMI connector
     * found with the getRMIConnectorsNames method.
     * Return null if no RMI connector is available.
     */
    public static String getCurrentRMIConnectorName() {
        try {
            if (currentRMIConnectorName == null) { // may be not set yet
                HashSet connectorNames = (HashSet)getRMIConnectorsNames();
                if (! connectorNames.isEmpty()) {
                    // Pick up the first connector name in the list
                    String firstName = (String)connectorNames.toArray()[0];
                    setCurrentRMIConnectorName(firstName);
                } // else, currentRMIConnector rests null
            }
            return currentRMIConnectorName;
        } catch (javax.naming.NamingException ne) {
            return null;
        }
    }

    /**
     * Set the currentRMIConnectorName to the specified value, then lookup for the RMI connector
     */
    public static void setCurrentRMIConnectorName(String name) throws NamingException {
        currentRMIConnectorName = name;
        // The rmi connector name may have changed, so we lookup for the corresponding rmi connector
        lookupRMIConnector();
    }

    /**
     * Set the currentRMIConnectorName to null
     */
    public static void resetCurrentRMIConnectorName() {
        currentRMIConnectorName = null;
    }

    /**
     * @return Set a set containning all RMI connector names available in the current context.
     */
    public static Set getRMIConnectorsNames() throws javax.naming.NamingException {
        try {
            HashSet res = new HashSet();
            Context ctx = getContext();
            // looking for all object registered in this registry
            for (NamingEnumeration e = ctx.list(""); e.hasMoreElements(); ) {
                String name = ((NameClassPair)e.nextElement()).getName();
                // we select only rmi connectors (start with 'RMIConnector')
                if (name.startsWith("RMIConnector"))
                    res.add(name);
            }
            return res;
        } catch (javax.naming.NamingException ne) {
            throw new javax.naming.NamingException("Cannot enumerates the names bound in the named context: '"
                                                   +getJonasNamingServiceURL()
                                                   +"' (registry probably not launched).");
        }
    }

    /**
     * @return String the value of the PROVIDER_URL property in the current context.
     * If a javax.naming.NamingException is catch, the exception message is returned.
     */
    public static String getJonasNamingServiceURL() {
        try {
            return (String)getContext().getEnvironment().get(Context.PROVIDER_URL);
        } catch (javax.naming.NamingException ne) {
            return ne.getMessage();
        }
    }

    /**
     * Create a new naming context based on the given env. properties
     * @param env properties to create a new naming context
     */
    public static void setNamingEnvCtx(Properties env) throws javax.naming.NamingException {
        context = new InitialContext(env);
        rmic = null;
        currentRMIConnectorName = null;
    }

    /**
     * Sets  the PROVIDER_URL property to the specified value. Then, sets the current RMI connector to null.
     */
    public static void setJonasNamingServiceURL(String url) throws javax.naming.NamingException {

        // Properties for the new naming context
        Properties p = new Properties();

        // Current context
        Context ctx = getContext();
        // Re-use the same values for the INITIAL_CONTEXT_FACTORY and URL_PKG_PREFIXES
        p.put(Context.INITIAL_CONTEXT_FACTORY, ctx.getEnvironment().get(Context.INITIAL_CONTEXT_FACTORY));
        p.put(Context.URL_PKG_PREFIXES, ctx.getEnvironment().get(Context.URL_PKG_PREFIXES));
        // Use the url parameter for the PROVIDER_URL property
        p.put(Context.PROVIDER_URL, url);

        try {
            context = new InitialContext(p);
        } catch (NamingException e) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Cannot create context : " + e);
                logger.log(BasicLevel.DEBUG, "Environment used :");
                logger.log(BasicLevel.DEBUG, Context.INITIAL_CONTEXT_FACTORY + " = " + p.get(Context.INITIAL_CONTEXT_FACTORY));
                logger.log(BasicLevel.DEBUG, Context.URL_PKG_PREFIXES + " = " + p.get(Context.URL_PKG_PREFIXES));
                logger.log(BasicLevel.DEBUG, Context.PROVIDER_URL + " = " + p.get(Context.PROVIDER_URL));
            }
            throw e;
        }

        rmic = null;
        currentRMIConnectorName = null;

    }

    /**
     * @return RMIConnector the current RMI connector, if null, lookup for a new one.
     */
    public static RMIConnector getRMIConnector() throws NamingException {
        if (rmic == null) {
            lookupRMIConnector();
        }
        if (rmic == null) {
            logger.log(BasicLevel.DEBUG, "Null RMIConnector");
        }
        return rmic;
    }

    /**
     * Lookup for the RMI connector registered with the currentRMIConnectorName JNDI name.
     */
    public static void lookupRMIConnector() throws NamingException {
        String curr = getCurrentRMIConnectorName();
        if (curr != null) {
            rmic = (RMIConnector)PortableRemoteObject.narrow(getContext().lookup(curr), RMIConnector.class);
        }
    }
}
