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

import java.io.InputStream;
import java.io.IOException;
import java.lang.ClassLoader;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.util.Properties;

import org.objectweb.common.Env;
import org.objectweb.jonas.common.Log;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.BasicLevel;


/**
 * Registry Service implementation
 * @author Helene Joanin
 * Contributor(s): 
 */

public class RegistryManager {

    static private Logger logger = null;
    private static final int DEFAULT_PORT_SUNRMI = 1099;
    private static final int DEFAULT_PORT_JEREMIE = 12340;

    private int port;
    private Registry registry = null;     // Needed to keep the object reference of the registry

    // Is the registry launched by the RegistryManager ?
    private boolean regStartedByMg = false;


    // ------------------------------------------------------------------
    // Singleton management:
    // - the constructor is private.
    // - use static method getInstance to retrieve/create the instance.
    // ------------------------------------------------------------------
    private static RegistryManager unique = null;

    /**
     * Create the Registry Manager.
     * Init the port number to use. It is getting via the jndi.properties file
     */
    private RegistryManager() {
	logger = Log.getLogger(Log.JONAS_REGISTRY_PREFIX);
	if (Env.isSunRMI()) {
	    port = DEFAULT_PORT_SUNRMI;
	} else {
	    port = DEFAULT_PORT_JEREMIE;
	}
	ClassLoader loader = RegistryManager.class.getClassLoader();
	if (loader == null) {
	    loader = ClassLoader.getSystemClassLoader();
	}
	InputStream is = loader.getResourceAsStream("jndi.properties");
	if (is != null) {
	    Properties props = new Properties();
	    try {
		props.load(is);
		String url = props.getProperty("java.naming.provider.url");
                logger.log(BasicLevel.DEBUG, "java.naming.provider.url : "+url);
                String factory = props.getProperty("java.naming.factory.initial");
                logger.log(BasicLevel.DEBUG, "java.naming.factory.initial : "+factory);
                String pkgs = props.getProperty("java.naming.factory.url.pkgs");
                if(pkgs != null)
                    logger.log(BasicLevel.DEBUG, "java.naming.factory.url.pkgs : "+pkgs);
                else
                    logger.log(BasicLevel.DEBUG, "java.naming.factory.url.pkgs : null");
		if (url != null) {
		    port = getPortOfUrl(url);
		} else {
		    logger.log(BasicLevel.ERROR, "RegistryManager init: Cannot find the 'java.naming.provider.url' property in the 'jndi.properties' file, "
				+ "default port '"+port+"' used");
		}
	    } catch (IOException e) {
		logger.log(BasicLevel.ERROR, "RegistryManager init: Cannot read 'jndi.properties' file, "
			    + "default port '"+port+"' used; ("+e+")");
	    }
	} else {
	    logger.log(BasicLevel.ERROR, "RegistryManager init: Cannot find 'jndi.properties' file, "
			+ "default port '"+port+"' used");
	}
	logger.log(BasicLevel.DEBUG,  "RegistryManager initialized with port number "+port);
    }

    public static RegistryManager getInstance() {
	if (unique == null) {
	    unique = new RegistryManager();
	}
	return unique;
    }


    /**
     * Parses the given RMI/JEREMIE url, and returns the port number.
     * (The default port number is given in error case)
     */
    static int getPortOfUrl(String url) {
	int portNumber;
	String protocol;
	if (Env.isSunRMI() || Env.isDavid()) {
	    protocol = "rmi";
	    portNumber = DEFAULT_PORT_SUNRMI;
	} else {
	    protocol = "jrmi";
	    portNumber = DEFAULT_PORT_JEREMIE;
	}
	if (!url.startsWith(protocol)) {
	    logger.log(BasicLevel.ERROR, "RegistryManager: Invalid URL '"+url+"' in the jndi.properties file, "
			    + "default port '"+portNumber+"' used");
	    return(portNumber);
	}
	int dInd = protocol.length()+1;
	if (url.startsWith("//", dInd)) { // url contains a domain
            int n = url.indexOf("/",dInd+2);
            String domain;
            if (n >= 0) {	// retrieve the domain
		domain = url.substring(dInd+2, n);
            } else {	// retrieve the domain
		domain = url.substring(dInd+2);
            }
            int m = domain.indexOf(":");
            if (m >= 0)	{ // domain is '[host]:[port]'
		portNumber = Integer.parseInt(domain.substring(m+1));
            } // else domain is '[host]'
	} // else url is 'protocol:[/][object]'

	return(portNumber);
    }


   /**
    * Start the Registry ie
    * Invoke dynamically the LocateRegistry.createRegistry(port) method.
    * (The LocateRegistry class differs on RMI/JEREMIE).
    *
    * @param    ignoreError  if true, ignore error at the registry starting.
    */
    public void start(boolean ignoreError) throws Exception {
	// Get the right LocateRegistry class and the LocateRegistry.createRegistry(int) method
	String locRegClassName = null;
	Class locRegClass = null;
	ClassLoader cl = this.getClass().getClassLoader();
	if (cl == null) {
	    cl = ClassLoader.getSystemClassLoader();
	}
	Method createMeth = null;
	if (Env.isSunRMI()) {
	    locRegClassName = "java.rmi.registry.LocateRegistry";
	} else {
	    locRegClassName = "org.objectweb.jeremie.libs.services.registry.LocateRegistry";
	}
	try {
	    locRegClass = cl.loadClass(locRegClassName);
	    Class[] paramsType = new Class[] {java.lang.Integer.TYPE};
	    createMeth = locRegClass.getMethod("createRegistry", paramsType);
	} catch (Exception e) {
	    throw new Exception("Error when starting the Registry: "
				+"Cannot get the LocateRegistry.createRegistry(int) method ("+e+")");
	}
	// Invoke the LocateRegistry.createRegistry(port) method
	try {
	    Object[] params = new Object[] {new Integer(port)};
	    registry = (Registry)createMeth.invoke(locRegClass, params);
	    regStartedByMg = true;
	    logger.log(BasicLevel.WARN, "Registry started on port '"+port+"'");
	} catch (InvocationTargetException e) {
	    if (ignoreError) {
		logger.log(BasicLevel.ERROR, "Warning: Registry already started on port '"+port+"'");
	    } else {
		throw new Exception("Error when starting the Registry: "
				    +"Cannot start the registry ("+e.getTargetException() +")");
	    }
	} catch (Exception e) {
	    throw new Exception("Error when starting the Registry: "
				+"Cannot invoke the "+createMeth+" method ("+e+")");
	}

    }

    /**
     * Stop the Registry started by the start method ie
     * Invoke dynamically the UnicastRemoteObject.unexportObject(Registry, true) method.
     * (The UnicastRemoteObject class differs on RMI/JEREMIE).
     */
    public void stop() throws Exception {

	if (regStartedByMg) {
	    // Get the right UnicastRemoteObject class
	    // and the UnicastRemoteObject.unexportObject(Registry, boolean) method
	    String ucroClassName = null;
	    Class ucroClass = null;
	    ClassLoader cl = this.getClass().getClassLoader();
	    if (cl == null) {
		cl = ClassLoader.getSystemClassLoader();
	    }
	    Method unexportMeth = null;
	    if (Env.isSunRMI()) {
		ucroClassName = "java.rmi.server.UnicastRemoteObject";
	    } else {
		ucroClassName = "org.objectweb.jeremie.libs.binding.moa.UnicastRemoteObject";
	    }
	    try {
		ucroClass = cl.loadClass(ucroClassName);
		Class[] paramsType = new Class[] {java.rmi.Remote.class, Boolean.TYPE};
		unexportMeth = ucroClass.getMethod("unexportObject", paramsType);
	    } catch (Exception e) {
		throw new Exception("Error when stoping the Registry: "
				    +"Cannot get theUnicastRemoteObject.unexportObject(Remote, boolean) method ("+e+")");
	    }
	    // Invoke the  method UnicastRemoteObject.unexportObject(registry, true)
	    try {
		Object[] params = new Object[] {registry, Boolean.TRUE};
		Boolean isOk = (Boolean)unexportMeth.invoke(ucroClass, params);
		if (!isOk.booleanValue()) {
		    throw new Exception("Cannot stop the registry");
		}
	    } catch (InvocationTargetException e) {
		throw new Exception("Error when stopping the Registry: "
				    +"Cannot stop the registry ("+e.getTargetException() +")");
	    } catch (Exception e) {
		throw new Exception("Error when stopping the Registry: "
				    +"Cannot invoke the "+unexportMeth+" method ("+e+")");
	    }

	    logger.log(BasicLevel.DEBUG,  "Registry on port '"+port+"' stopped");
	}

    }

}

