/**
 * JOnAS : Java(TM) OpenSource Application Server
 * Copyright (C) 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.ews;

import java.io.File;
import java.util.Iterator;
import java.util.List;

import org.ow2.jonas.deployment.ws.JaxRpcPortComponentDesc;
import org.ow2.jonas.deployment.ws.PortComponentDesc;
import org.ow2.jonas.deployment.ws.ServiceDesc;
import org.ow2.jonas.generators.genbase.GenBaseException;
import org.ow2.jonas.generators.genbase.archive.Archive;
import org.ow2.jonas.generators.genbase.archive.J2EEArchive;
import org.ow2.jonas.generators.genbase.archive.WebApp;
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.WsEndpointDDModifier;
import org.ow2.jonas.generators.wsgen.generator.WsEndpointGenerator;
import org.ow2.jonas.generators.wsgen.generator.ews.wsdltoj2ee.JOnASJ2EEWebServicesContext;
import org.ow2.jonas.generators.wsgen.generator.ews.wsdltoj2ee.emitter.FullEmitter;
import org.ow2.jonas.generators.wsgen.generator.ews.wsdltoj2ee.factory.JOnASEndpointGeneratorFactory;
import org.ow2.jonas.lib.loader.AbsModuleClassLoader;
import org.ow2.jonas.lib.util.I18n;
import org.ow2.jonas.lib.util.Log;
import org.w3c.dom.NodeList;

import org.apache.axis.constants.Scope;
import org.apache.axis.wsdl.toJava.GeneratedFileInfo;
import org.apache.ws.ews.mapper.J2eeGeneratorFactory;
import org.apache.ws.ews.mapper.context.JAXRPCMapperContext;




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

/**
 * Generate Axis specific config files for Endpoint
 * @author Guillaume Sauthier
 */
public class EWSWsEndpointGenerator extends WsEndpointGenerator {

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

    /**
     * Axis Servlet classname
     */
    private static final String AXIS_SERVLET_CLASS = "org.ow2.jonas.ws.axis.JAxisServlet";

    /**
     * init-param name for declaring server configuration file
     */
    private static final String SERVER_CONFIG = "axis.serverConfigFile";

    /**
     * generated server-config file
     */
    private File generatedServerConfig;

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

    /**
     * Creates a new AxisWsEndpointGenerator
     * @param config Generator Configuration
     * @param serviceDesc WebService Endpoint description
     * @param ddm Web DD Modifier
     * @param wsddm webservices.xml DD modifier
     * @param arch base archive
     * @throws GenBaseException When instanciation fails
     */
    public EWSWsEndpointGenerator(Config config, ServiceDesc serviceDesc, WsEndpointDDModifier ddm,
            WebServicesDDModifier wsddm, Archive arch) throws GenBaseException {
        super(config, serviceDesc, ddm, wsddm, arch);
    }

    /**
     * Generate server side configuration file
     * @throws WsGenException When generation fails
     */
    public void generate() throws WsGenException {

        // 1. Generation of wsdd server file
        String filename = null;
        try {
            FullEmitter emitter = new FullEmitter();

            emitter.setWscontext(prepareJ2EEWebServicesContext(getService()));
            emitter.setMappingFileInputStream(getService().getMappingFileURL().openStream());
            emitter.setOutputDir(this.getSources().getCanonicalPath());
            J2eeGeneratorFactory factory = new JOnASEndpointGeneratorFactory();
            factory.setEmitter(emitter);
            emitter.setFactory(factory);
            emitter.setTypeMappingVersion("1.3");
            emitter.setScope(Scope.REQUEST);
            emitter.setWrapArrays(true);

            emitter.setServerSide(true);
            emitter.run(getService().getLocalWSDLURL().toExternalForm());

            generatedServerConfig = new File(findServerConfigFile(emitter.getGeneratedFileInfo()));
            filename = generatedServerConfig.getName();
            getLogger().log(BasicLevel.INFO,
                    "Webservice endpoint WSDD file '" + filename + "' sucessfully generated by EWS.");
        } catch (Exception e) {
            String err = i18n.getMessage("EWSWsEndpointGenerator.generate.WSDL2Java", getService().getName(), e
                    .getMessage());
            logger.log(BasicLevel.ERROR, err, e);
            throw new WsGenException(err, e);
        }

        // 2. classpath creation
        J2EEArchive j2eeArchive = (J2EEArchive) getArchive();
        AbsModuleClassLoader cl = (AbsModuleClassLoader) j2eeArchive.getModuleClassloader();
        getConfig().setClasspath(getConfig().getClasspath() + cl.getClasspath());

        // 3. Remove existing servlets
        //  * remove the servlet used by the port (store security informations)
        //  * remove the associated servlet-mapping (store old url-pattern value)
        for (Iterator i = getService().getPortComponents().iterator(); i.hasNext();) {
            PortComponentDesc desc = (PortComponentDesc) i.next();

            String urlPatternOld = null;
            NodeList securityRoleRefs = null;

            // port-component linked to a servlet :
            if (desc.hasJaxRpcImpl()) {
                JaxRpcPortComponentDesc jax = (JaxRpcPortComponentDesc) desc;

                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Removing servlet '" + jax.getSibLink() + "'");
                }

                // remove servlet mapping
                urlPatternOld = getModifier().removeServletMapping(jax.getSibLink());

                // remove old servlet but pick up its security role refs
                //getModifier().removeServlet(jax.getSibLink());
                securityRoleRefs = getModifier().removeServletWithSecurity(jax.getSibLink());
            }

            // in all case :
            // create a servlet with all needed configuration
            // and use our own
            getModifier().addServlet(desc.getSibLink(), AXIS_SERVLET_CLASS);
            getModifier().addServletParam(desc.getSibLink(), SERVER_CONFIG, filename);
            getModifier().addServletParam(desc.getSibLink(), "axis.development.system", "true");

            String mapping = null;
            if (desc.getEndpointURI() != null) {
                // simple style
                mapping = desc.getEndpointURI();
            } else {
                // old style
                mapping = "/" + desc.getName() + "/*";
            }
            getModifier().addServletMapping(desc.getSibLink(), mapping);

            if (desc.hasJaxRpcImpl()) {
                // add the first element in the servletToSecurityRoleRefs map
                // (currently consider that only one servlet with security
                // constraints was removed !! - if there were more, keep the
                // constraints of the last removed)
                if (securityRoleRefs != null) {
                    getModifier().addServletSecurityRoleRefs(desc.getSibLink(), securityRoleRefs);
                }
                // update url-pattern
                getModifier().updateSecurityConstraint(urlPatternOld, mapping);
            }
        }
    }

    /**
     * @param generatedFileInfo files generated by Axis
     * @return Returns the first filename that matches "deploy-server-*.wsdd"
     */
    private static String findServerConfigFile(GeneratedFileInfo generatedFileInfo) {
        List generated = generatedFileInfo.getFileNames();
        boolean found = false;
        String filename = null;
        for (Iterator i = generated.iterator(); i.hasNext() && !found;) {
            String entry = (String) i.next();
            // */deploy-server-*.wsdd
            if (entry.matches(".*deploy-server-\\d+\\.wsdd$")) {
                found = true;
                filename = entry;
            }
        }
        return filename;
    }

    /**
     * @param service the ServiceDesc to be used in J2EE WS Context
     * @return Returns a configured J2EEWebServiceContext
     */
    private JAXRPCMapperContext prepareJ2EEWebServicesContext(ServiceDesc service) {
        JOnASJ2EEWebServicesContext ctx = new JOnASJ2EEWebServicesContext();
        ctx.setServiceDesc(service);
        return ctx;
    }

    /**
     * Add generated files in given Archive
     * @param archive WebApp archive
     * @throws WsGenException When cannot add files in archive
     */
    public void addFiles(Archive archive) throws WsGenException {
        // archive must be a WebApp
        if (!(archive instanceof WebApp)) {
            String err = getI18n().getMessage("AxisWsEndpointGenerator.addFiles.illegal", archive.getRootFile());
            throw new IllegalArgumentException(err);
        }

        WebApp web = (WebApp) archive;
        web.addFileIn("WEB-INF/", generatedServerConfig);

        web.addDirectoryIn("WEB-INF/classes/", getClasses());
        web.addDirectoryIn("WEB-INF/sources/", getSources());
    }
}