/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2009 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.ws.axis;

import org.apache.axis.EngineConfiguration;
import org.apache.axis.EngineConfigurationFactory;
import org.apache.axis.configuration.EngineConfigurationFactoryServlet;
import org.apache.axis.deployment.wsdd.WSDDDeployment;
import org.apache.axis.deployment.wsdd.WSDDService;
import org.apache.axis.encoding.SerializationContext;
import org.apache.axis.utils.XMLUtils;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.jonas.lib.util.Log;
import org.w3c.dom.Document;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;


/**
 * This is the JOnAS implementation of EngineConfigurationFactory for Servlet.
 * It override Axis default one (EngineConfigurationFactoryServlet).
 *
 * @author Guillaume Sauthier
 * @author Xavier Delplanque
 */
public class JServletEngineConfigurationFactory
    implements EngineConfigurationFactory {

    /** server config parameter name in init-param */
    public static final String AXIS_SERVER_CONFIG_PARAM = "axis.serverConfigFile";

    /** server-config.wsdd base */
    public static final String SERVER_CONFIG_WSDD = "org/ow2/jonas/ws/axis/server-config.wsdd";

    /** The logger to use */
    private static Logger logger = Log.getLogger("org.ow2.jonas.ws");

    /** The file name to use for research */
    private String serverConfigFile;

    /** The EngineConfigurationFactoryServlet for delegation */
    private EngineConfigurationFactory delegate;

    /** The context used for this JServletEngineConfigurationFactory */
    private ServletContext ctx;

    /** The config used for this JServletEngineConfigurationFactory */
    private ServletConfig cfg;


    /**
     * Create the default engine configuration and detect whether the user has
     * overridden this with their own.
     *
     * @param conf a ServletConfig
     */
    protected JServletEngineConfigurationFactory(final ServletConfig conf) {
        // get the axis delegate
        cfg = conf;
        ctx = conf.getServletContext();
        delegate = EngineConfigurationFactoryServlet.newFactory(conf);
    }

    /**
     * Creates and returns a new JServletEngineConfigurationFactory. If a factory
     * cannot be created, return 'null'. The factory may return non-NULL only
     * if: <br>
     * - it knows what to do with the param (param != null) <br>
     * - it can find it's configuration information
     *
     * @param param The object used to retrieved the right Factory instance
     *
     * @return null if param is not a ServletContext, or return the JOnAS
     *         EngineConfigurationFactory used for a service-endpoint.
     */
    public static EngineConfigurationFactory newFactory(final Object param) {

        if (param == null) {
            return null; // not for us.
        }

        if (param instanceof JAxisServlet) {
            JAxisServlet servlet = (JAxisServlet) param;
            return new JServletEngineConfigurationFactory(servlet.getServletConfig());
        } else {
            return null;
        }
    }

    /**
     * Get a server engine configuration. Try to load it from a File or as a
     * ServletContext Resource. Delegate to EngineConfigurationFactoryServlet
     * for default behavior.
     *
     * @return a server side EngineConfiguration
     */
    public EngineConfiguration getServerEngineConfig() {

        logger.log(BasicLevel.DEBUG, "Entering getServerEngineConfig for servlet " + cfg.getServletName());

        try {

            // retrieve init param specifying server-config.wsdd filename to be loaded
            serverConfigFile = cfg.getInitParameter(AXIS_SERVER_CONFIG_PARAM);

            logger.log(BasicLevel.DEBUG, "serverConfigFile=" + serverConfigFile);

            // if no config file specified, delegate to Axis Servlet EngineConfigurationFactory
            if (serverConfigFile == null) {
                return delegate.getServerEngineConfig();
            }

            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG,
                           "Loading server-config file '" + serverConfigFile + "'");
            }

            /*
             * Use the WEB-INF directory
             * (so the config files can't get snooped by a browser)
             */
            String appWebInfPath = "/WEB-INF";

            Document deploy = null;

            String realWebInfPath = ctx.getRealPath(appWebInfPath);

            /**
             * If path/file doesn't exist, it may still be accessible
             * as a resource-stream (i.e. it may be packaged in a JAR
             * or WAR file).
             */
            if (realWebInfPath == null
                || !(new File(realWebInfPath, serverConfigFile)).exists()) {

                String name = appWebInfPath + "/" + serverConfigFile;
                InputStream is = ctx.getResourceAsStream(name);

                if (is != null) {
                    deploy = XMLUtils.newDocument(is);
                }

                if (deploy == null) {
                    String err = "Cannot get config file '" + serverConfigFile + "' as Resource.";
                    logger.log(BasicLevel.ERROR, err);
                }
            }

            /**
             * Couldn't get data  OR  file does exist.
             * If we have a path, then attempt to either open
             * the existing file, or create an (empty) file.
             */
            if (deploy == null  &&  realWebInfPath != null) {
                try {
                    InputStream is = new FileInputStream(new File(realWebInfPath, serverConfigFile));
                    deploy = XMLUtils.newDocument(is);
                } catch (IOException e) {
                    String err = "Cannot get config file '" + serverConfigFile + "' as File Resource.";
                    logger.log(BasicLevel.ERROR, err);
                }
            }

            // if all attempts fails, delegate to Axis...
            if (deploy == null) {
                return delegate.getServerEngineConfig();
            }

            // get Base Deployment desc as Resource of ClassLoader
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            InputStream is = cl.getResourceAsStream(SERVER_CONFIG_WSDD);
            Document base = XMLUtils.newDocument(is);

            // Combine the 2 DD
            WSDDDeployment deployWSDD = new WSDDDeployment(deploy.getDocumentElement());
            WSDDDeployment configWSDD = new WSDDDeployment(base.getDocumentElement());

            // Define the disablePrettyXML parameter at service level because global
            // parameters of WSDD aren't used to build message context properties
            WSDDService[] services = deployWSDD.getServices();
            for (int j = 0; j < services.length; j++) {
                WSDDService service = services[j];
                service.setParameter("disablePrettyXML", "true");
            }

            deployWSDD.deployToRegistry(configWSDD);

            if (logger.isLoggable(BasicLevel.DEBUG)) {
                StringWriter sw = new StringWriter();
                SerializationContext ctx = new SerializationContext(sw);

                try {
                    configWSDD.writeToContext(ctx);
                } catch (Exception ioe) {
                    logger.log(BasicLevel.DEBUG, "Cannot serialize Axis wsdd for servlet " + cfg.getServletName());
                }
                logger.log(BasicLevel.DEBUG, sw.getBuffer().toString());
            }

            return configWSDD;
        } catch (Exception e) {
            // if exception occurs, set a default server-config.wsdd
            String err = "Cannot configure axis server from '" + serverConfigFile + "'. Use axis default. Caused by : " + e.getMessage();
            e.printStackTrace();
            logger.log(BasicLevel.ERROR, err);
            return delegate.getServerEngineConfig();
        }
    }

    /**
     * Return null. (Not used for WebService server-side)
     *
     * @return null
     */
    public EngineConfiguration getClientEngineConfig() {
        return null;
    }
}
