/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2007 Bull S.A.S.
 * Contact: jonas-team@ow2.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.ow2.jonas.lib.management.domain.proxy;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Map;

import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.naming.Context;
import javax.naming.InitialContext;

import org.omg.CORBA.ORB;
import org.ow2.jonas.lib.execution.ExecutionResult;
import org.ow2.jonas.lib.execution.IExecution;
import org.ow2.jonas.lib.execution.RunnableHelper;
import org.ow2.jonas.lib.loader.OSGiClassLoader;

import org.ow2.carol.util.configuration.ConfigurationRepository;
import org.ow2.carol.util.configuration.Protocol;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.jonas.lib.util.Log;
/**
 * JMX Connector related utility methods.
 * @author Guillaume Sauthier
 */
public class ConnectorUtils {

    /**
     * Private default constructor for utility class.
     */
    private ConnectorUtils() { }

    /**
     * Extract the protocol from a JMX connector URL.
     * @param url JMX connector url to parse
     * @return protocol associated with the url
     */
    public static String getProtocolFromJmxConnectorUrl(final String url) {

        int proIndMin = url.indexOf("/jndi/") + "/jndi/".length();
        int proIndMax = url.indexOf("://");
        String protocol = url.substring(proIndMin, proIndMax);

        // for the RMI protocol, we have to parse the connector name to identify
        // the underlaying
        // protocol : either jrmp or irmi
        if (protocol.equals("rmi")) {
            String subUrl = url.substring(proIndMax + "://".length());
            int subProIndMin = subUrl.indexOf("/") + "/".length();
            int subProIndMax = subUrl.indexOf("connector_");
            protocol = subUrl.substring(subProIndMin, subProIndMax);
        }

        return protocol;
    }

    /**
     *
     * Get the provider url from a JMX connector URL.
     * @param url JMX connector url to parse
     * @return provider url associated with the url
     */
    public static String getProviderUrlFromJmxConnectorUrl(final String url) {

        int proIndMin = url.indexOf("/jndi/") + "/jndi/".length();
        String protocol = getProtocolFromJmxConnectorUrl(url);
        int urlIndMax = url.indexOf("/" + protocol + "connector");
        return url.substring(proIndMin, urlIndMax);
    }

    /**
     * Create a connector client for the connector server at the given address, and establish connection.
     * @param urlString connector server address
     * @param logger caller provided logger
     * @param username user name (or null)
     * @param password decoded password (or null)
     * @return created connector if connection established, null otherwise
     * @throws IOException if the connector client or the connection cannot be made because of a communication problem.
     * @throws SecurityException if the connection cannot be made for security reasons.
     */
    public static JMXConnector getConnector(final String urlString, final String username
            , final String password, final Logger logger)
        throws IOException, SecurityException, MalformedURLException {
        JMXServiceURL url = null;
        try {
            url = new JMXServiceURL(urlString);
            return getConnector(url, username, password, logger);
        } catch (MalformedURLException me) {
            logger.log(BasicLevel.ERROR, "Malformed URL:" + urlString);
            throw me;
        } catch (SecurityException e) {
            throw e;
        } catch (IOException e) {
            throw e;
        }
    }
    /**
     * Create a connector client for the connector server at the given address, and establish connection.
     * @param url connector server address
     * @param logger caller provided logger
     * @param username user name (or null)
     * @param password decoded password (or null)
     * @return created connector if connection established, null otherwise
     * @throws IOException if the connector client or the connection cannot be made because of a communication problem.
     * @throws SecurityException if the connection cannot be made for security reasons.
     */
    public static JMXConnector getConnector(final JMXServiceURL url, final String username
            , final String password, final Logger logger)
        throws IOException, SecurityException {
        JMXConnector connector = null;
        // Create environment for connection
        Map env = new HashMap();
        if (url.getProtocol().equals("iiop")) {
            try {
                ORB orb = null;

                IExecution<ORB> exec = new IExecution<ORB>() {
                    public ORB execute() throws Exception {
                        ORB obj = (ORB) new InitialContext().lookup("java:comp/ORB");
                        return obj;
                    }
                };

                ClassLoader cl = new OSGiClassLoader();
                ExecutionResult<ORB> res = RunnableHelper.execute(cl, exec);
                if (res.hasException()) {
                    throw res.getException();
                }
                orb = res.getResult();

                env.put("java.naming.corba.orb", orb);
            } catch (Exception e) {
                logger.log(BasicLevel.ERROR, "Cannot get ORB: : " + e);
                return null;
            }
        }
        String initCtxClass = null;
        String providerUrl = null;
        try {
            String urlPath = url.getURLPath();
            String protocol = ConnectorUtils.getProtocolFromJmxConnectorUrl(urlPath);
            Protocol p = ConfigurationRepository.getProtocol(protocol);
            initCtxClass = p.getInitialContextFactoryClassName();
            providerUrl = ConnectorUtils.getProviderUrlFromJmxConnectorUrl(urlPath);
        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "Unable to get the InitialContext from the protocol '"
                    + url.getProtocol() + "'");
            return null;
        }
        if (initCtxClass != null) {
            env.put(Context.INITIAL_CONTEXT_FACTORY, initCtxClass);
            env.put(Context.PROVIDER_URL, providerUrl);
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Setting the InitialContext to " + initCtxClass);
            }
        }
        // add authentication info
        if (username != null && password != null) {
            String[] creds = {username, password};
            env.put(JMXConnector.CREDENTIALS, creds);
        }
        // Create connector
        try {
            final JMXServiceURL myUrl = url;
            final Map myEnv = env;
            IExecution<JMXConnector> exec = new IExecution<JMXConnector>() {

                public JMXConnector execute() throws Exception {
                    return JMXConnectorFactory.connect(myUrl, myEnv);
                }

            };
            ClassLoader cl = JMXProxy.class.getClassLoader();
            ExecutionResult<JMXConnector> res = RunnableHelper.execute(cl, exec);
            if (res.hasException()) {
                throw res.getException();
            }
            connector = res.getResult();
        } catch (IOException e) {
            throw e;
        } catch (SecurityException e) {
            throw e;
        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "JMXConnectorFactory error: " + e);
        }
        return connector;
    }
}
