/**
 * 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): Guillaume Sauthier
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.objectweb.jonas.jaxr;

import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.objectweb.jonas.common.JProp;
import org.objectweb.jonas.common.Log;
import org.objectweb.jonas.common.PropDump;
import org.objectweb.jonas.service.AbsServiceImpl;
import org.objectweb.jonas.service.ServiceException;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;


/**
 * <code>JAXRService</code> Implementation.
 * @author Guillaume Sauthier
 */
public class JAXRServiceImpl extends AbsServiceImpl implements JAXRService {

    /**
     * Initial Context for Naming
     */
    private Context ictx = null;
    /**
     * Logger for this service.
     */
    private static Logger logger = null;

    /**
     * List of the factories names to load when starting the MailService
     */
    private Vector factoryNames = new Vector();

    /**
     *  List of the binded factories (jndi name -> factory name)
     */
    private Map bindedFactories = new Hashtable();
    /**
     * JAXR service configuration parameters (factories)
     */
    public static final String FACTORIES = "jonas.service.jaxr.factories";

    /**
     * JAXR service configuration parameters (class)
     */
    public static final String CLASS = "jonas.service.jaxr.class";

    /**
     * JAXR ConnectionFactory JNDI name
     */
    private static final String JNDI_NAME = "jaxr.jndi.name";

    /**
     * @see org.objectweb.jonas.service.AbsServiceImpl#doInit(javax.naming.Context)
     */
    protected void doInit(Context ctx) throws ServiceException {
        // get logger for this service
        logger = Log.getLogger(Log.JONAS_JAXR_PREFIX);

        //Get the inital Context
        try {
            ictx = new InitialContext();
        } catch (NamingException e) {
            logger.log(BasicLevel.ERROR, "Cannot create initial context during the jaxr service initializing");
            throw new ServiceException("Cannot create initial context during the jaxr service initializing", e);
        }

        // Get the list of the factory names
        String factories = null;
        try {
            factories = (String) ctx.lookup(FACTORIES);
        } catch (NamingException e) {
            ; // No problem if there is no value for 'factories'
        }
        if (factories != null) {
            String[] names = factories.split(", ");
            for (int i = 0; i < names.length; i++) {
                factoryNames.add(names[i].trim());
            }
        }
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "jaxr service initialized");
        }

    }

    /**
     * @see org.objectweb.jonas.service.AbsServiceImpl#doStart()
     */
    protected void doStart() throws ServiceException {
        // creates each factory
        String factoryName = null;
        for (Iterator i = factoryNames.iterator(); i.hasNext();) {
            factoryName = (String) i.next();
            try {
                JProp prop = JProp.getInstance(factoryName);
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Creating JAXR Connection Factory " + factoryName);
                }
                createJAXRConnection(prop.getConfigFileEnv());
            } catch (Exception e) {
                if (logger.isLoggable(BasicLevel.ERROR)) {
                    logger.log(BasicLevel.ERROR, "JOnAS: Cannot create jaxr factory " + factoryName + " : " + e);
                    logger.log(BasicLevel.ERROR, "Please check the " + factoryName + ".properties file");
                }
            }
        }
    }

    /**
     * @see org.objectweb.jonas.service.AbsServiceImpl#doStop()
     */
    protected void doStop() throws ServiceException {
        removeAllJAXRConnections();
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "jaxr service stopped");
        }
    }

    /**
     * Remove all Binded JAXRConnections
     */
    private void removeAllJAXRConnections() {

        for (Iterator i = bindedFactories.keySet().iterator(); i.hasNext();) {
            String name = (String) i.next();
            removeJAXRConnection(name);
        }

    }

    /**
     * @see org.objectweb.jonas.jaxr.JAXRService#createJAXRConnection(java.util.Properties)
     */
    public void createJAXRConnection(Properties props) throws ServiceException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            PropDump.print("These are the properties from which the jaxrService picks to construct jaxr Factories", props, logger, BasicLevel.DEBUG);
        }

        //Factory type/jndi name must be non null
        String jndiName = props.getProperty(JNDI_NAME);

        if (jndiName == null) {
            String err = "The property '" + JNDI_NAME + "' is a required property.";
            logger.log(BasicLevel.ERROR, err);
            throw new ServiceException(err);
        }

        // Verify that jndi name not already used
        if (bindedFactories.containsKey(jndiName)) {
            String err = "There is already a factory bound with the name " + jndiName + ", please correct the provided configuration properties";
            logger.log(BasicLevel.ERROR, err);
            throw new ServiceException(err);
        }

        // Create the JAXRConnection Object
        JAXRConnection jaxrConnection = new JAXRConnection(props);

        // Bind the factory object in the naming context
        bindJAXRFactory(jndiName, jaxrConnection);

        logger.log(BasicLevel.INFO, "Mapping JAXR Connection Factory on " + jndiName);

    }

    /**
     * Bind JAXRConnection into Registry and inside local Map.
     * @param jndiName jndiName of the connection
     * @param jaxrConnection the JAXRConnection to be bound
     */
    private void bindJAXRFactory(String jndiName, JAXRConnection jaxrConnection) {
        try {
            ictx.rebind(jndiName, jaxrConnection.getReference());
            bindedFactories.put(jndiName, jaxrConnection);
        } catch (NamingException e) {
            String err = "Cannot bind jaxr factory '" + jndiName + "'";
            logger.log(BasicLevel.ERROR, err);
            throw new ServiceException(err, e);
        }
    }

    /**
     * @see org.objectweb.jonas.jaxr.JAXRService#modifyJAXRConnection(java.lang.String, org.objectweb.jonas.jaxr.JAXRConnection)
     */
    public void modifyJAXRConnection(String name, JAXRConnection jaxrc) throws ServiceException {
        // remove old Connection
        removeJAXRConnection(name);
        // create new one
        bindJAXRFactory(name, jaxrc);
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "JAXRConnection modified");
        }
    }

    /**
     * @see org.objectweb.jonas.jaxr.JAXRService#removeJAXRConnection(java.lang.String)
     */
    public void removeJAXRConnection(String name) throws ServiceException {

        // test if factory is registered
        if (!bindedFactories.containsKey(name)) {
            String err = "Unknown JAXRConnection '" + name + "'";
            logger.log(BasicLevel.ERROR, err);
            throw new ServiceException(err);
        }

        // remove from the local list
        bindedFactories.remove(name);

        // remove from the registry
        try {
            ictx.unbind(name);
        } catch (NamingException e) {
            String err = "Cannot unbind JAXR Connection '" + name + "'";
            logger.log(BasicLevel.ERROR, err);
            throw new ServiceException(err, e);
        }

        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "JAXRConnection '" + name + "' removed");
        }
    }

}
