/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2003-2004 Bull S.A.
 * 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.generators.wsgen.generator;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.ow2.jonas.deployment.ws.ServiceDesc;
import org.ow2.jonas.deployment.ws.ServiceRefDesc;
import org.ow2.jonas.generators.genbase.GenBaseException;
import org.ow2.jonas.generators.genbase.archive.Archive;
import org.ow2.jonas.generators.genbase.generator.Config;
import org.ow2.jonas.generators.wsgen.WsGenException;
import org.ow2.jonas.generators.wsgen.ddmodifier.WebServicesDDModifier;
import org.ow2.jonas.generators.wsgen.ddmodifier.WsClientDDModifier;
import org.ow2.jonas.generators.wsgen.ddmodifier.WsEndpointDDModifier;
import org.ow2.jonas.lib.util.I18n;
import org.ow2.jonas.lib.util.Log;




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

/**
 * a <code>GeneratorFactory</code> has to be extended by specific generation
 * mecanism. It will look in <code>jonas.properties</code> file for a property
 * named :<code>jonas.service.ws.wsgen.generator.factory</code> that is a
 * classname extending <code>GeneratorFactory</code>. By default Axis
 * GeneratorFactory is used.
 *
 * @author Guillaume Sauthier
 */
public abstract class GeneratorFactory implements org.ow2.jonas.generators.genbase.generator.GeneratorFactory {

    /**
     * Generator Factory property name in jonas.properties
     */
    public static final String GENERATOR_FACTORY = "jonas.service.jaxrpc.wsgen.generator.factory";

    /**
     * Default GeneratorFactory impl to use
     */
    public static final String GENERATOR_FACTORY_DEFAULT = "org.ow2.jonas.generators.wsgen.generator.ews.EWSGeneratorFactory";

    /** <code>GeneratorFactory</code> unique instance */
    private static GeneratorFactory instance = null;

    /** i18n */
    private static I18n i18n = I18n.getInstance(GeneratorFactory.class);

    /** logger */
    private static Logger logger = Log.getLogger(Log.JONAS_WSGEN_PREFIX);

    /** Configuration to set on instanciated Generator */
    private Config configuration;

    /**
     * Returns the unique GeneratorFactory instance.
     *
     * @return the unique GeneratorFactory instance.
     *
     * @throws WsGenException When instanciation fails
     */
    public static GeneratorFactory getInstance() throws WsGenException {
        if (instance == null) {
            instance = newInstance();
        }

        return instance;
    }

    /**
     * Create a new generatorFactory instance by looking up in
     * <code>jonas.properties</code> and load class specified with
     * <code>jonas.service.ws.wsgen.generator-factory</code> property. If not
     * set, Axis GeneratorFactory is the default Factory returned.
     *
     * @return a new generatorFactory instance.
     *
     * @throws WsGenException when Exception occurs when dynamiccaly
     *         instantiating GeneratorFactory subclass
     */
    private static GeneratorFactory newInstance() throws WsGenException {

        String classname = null;

        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        InputStream is = cl.getResourceAsStream("jonas.properties");

        if (is == null) {
            // Should be on the client side
            // use default value
            classname = GENERATOR_FACTORY_DEFAULT;
        } else {
            Properties jprop = new Properties();
            try {
                jprop.load(is);
                classname = jprop.getProperty(GENERATOR_FACTORY, GENERATOR_FACTORY_DEFAULT);
            } catch (IOException e) {
                // cannot read properties, use default value
                logger.log(BasicLevel.WARN, "Cannot get '" + GENERATOR_FACTORY + "' value, default used : '"
                        + GENERATOR_FACTORY_DEFAULT + "'");
                classname = GENERATOR_FACTORY_DEFAULT;
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    // use default if it was not set
                    if (classname == null) {
                        classname = GENERATOR_FACTORY_DEFAULT;
                    }
                }
            }
        }

        // instanciate
        GeneratorFactory gf = null;

        try {
            Class<? extends GeneratorFactory> gfc = cl.loadClass(classname)
                                                      .asSubclass(GeneratorFactory.class);
            gf = gfc.newInstance();
        } catch (Exception e) {
            // Try with another ClassLoader
            cl = GeneratorFactory.class.getClassLoader();
            try {
                Class<? extends GeneratorFactory> gfc = cl.loadClass(classname)
                                                          .asSubclass(GeneratorFactory.class);
                gf = gfc.newInstance();
            } catch (Exception e2) {
                String err = i18n.getMessage("GeneratorFactory.newInstance.instance", classname);
                throw new WsGenException(err, e2);
            }
        }

        return gf;

    }

    /**
     * Return a new WsClientGenerator for the specific generation mecanism.
     *
     * @param serviceRef the service-ref containing information for client side
     *        generation process.
     * @param ddm the XML modifier.
     * @param archive the Archive to be modified
     *
     * @return a new WsClientGenerator.
     *
     * @throws GenBaseException When Factory cannot instanciate WsClientGenerator
     */
    public abstract WsClientGenerator newGenerator(ServiceRefDesc serviceRef, WsClientDDModifier ddm, Archive archive)
            throws GenBaseException;

    /**
     * Return a new WsEndpointGenerator for the specific generation mecanism.
     *
     * @param serviceDesc the webservice-description containing information for
     *        server side generation process.
     * @param ddm the XML modifier.
     * @param wsddm the Webservices.xml DD modifier
     * @param arch The archive to modify
     *
     * @return a new WsEndpointGenerator.
     *
     * @throws GenBaseException When Factory cannot instanciate WsEndpointGenerator
     */
    public abstract WsEndpointGenerator newGenerator(ServiceDesc serviceDesc, WsEndpointDDModifier ddm,
            WebServicesDDModifier wsddm, Archive arch) throws GenBaseException;

    /**
     * Set the Configuration to use with newly created Generator.
     *
     * @param config the Configuration to use with newly created Generator.
     */
    public void setConfiguration(Config config) {
        this.configuration = config;
    }

    /**
     * Get the Configuration to use with newly created Generator.
     *
     * @return the Configuration to use with newly created Generator
     */
    public Config getConfiguration() {
        return configuration;
    }
}
