/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2008-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.cxf.jaxws;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.transport.http.WSDLQueryHandler;
import org.apache.cxf.transports.http.QueryHandler;
import org.apache.cxf.transports.http.QueryHandlerRegistry;
import org.ow2.jonas.ws.cxf.http.JOnASDestination;
import org.ow2.jonas.ws.cxf.http.JOnASWSDLQueryHandler;
import org.ow2.jonas.ws.jaxws.IWSRequest;
import org.ow2.jonas.ws.jaxws.IWSResponse;
import org.ow2.jonas.ws.jaxws.IWebServiceEndpoint;
import org.ow2.jonas.ws.jaxws.PortMetaData;
import org.ow2.jonas.ws.jaxws.WSException;
import org.ow2.jonas.ws.jaxws.PortIdentifier;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;

/**
 * This class represents the JOnAS' view on a web service endpoint (server side).
 * @author Guillaume Sauthier
 */
public class CXFWSEndpoint implements IWebServiceEndpoint {

    /**
     * Logger.
     */
    private static Log logger = LogFactory.getLog(CXFWSEndpoint.class);

    /**
     * Wrapped CXF Endpoint.
     */
    private EndpointImpl endpoint = null;

    /**
     * The destination to execute.
     */
    private JOnASDestination destination = null;

    /**
     * Web service port description info.
     */
    private PortMetaData portMetaData;

    /**
     * Describe the WS type (EJB/POJO).
     */
    private IWebServiceEndpoint.EndpointType type;

    /**
     * The "parent" container.
     */
    private WebservicesContainer container;

    /**
     * Identifier.
     */
    private PortIdentifier identifier;

    /**
     * Construct a new WSEndpoint using CXF as JAX-WS implementation.
     * @param endpoint the wrapped CXF endpoint.
     * @param type is this endpoint a POJO or an EJB ?
     * @param pmd Metadata related to the port being exposed
     */
    public CXFWSEndpoint(final EndpointImpl endpoint,
                         final EndpointType type,
                         final PortMetaData pmd,
                         final WebservicesContainer container) {
        this.endpoint = endpoint;
        this.type = type;
        this.portMetaData = pmd;
        this.destination = (JOnASDestination) endpoint.getServer().getDestination();
        this.container = container;

        // Create the identifier
        this.identifier = new PortIdentifier(endpoint.getServiceName(),
                                             endpoint.getEndpointName().getLocalPart());
    }

    /* (non-Javadoc)
     * @see org.ow2.jonas.ws.jaxws.IWebServiceEndpoint#getType()
     */
    public EndpointType getType() {
        return type;
    }

    /**
     * @return the port identifier of this endpoint.
     */
    public PortIdentifier getIdentifier() {
        return this.identifier;
    }

    /* (non-Javadoc)
     * @see org.ow2.jonas.ws.jaxws.IWebServiceEndpoint#invoke(org.ow2.jonas.ws.jaxws.IWSRequest, org.ow2.jonas.ws.jaxws.IWSResponse)
     */
    public void invoke(final IWSRequest request, final IWSResponse response) throws WSException {

        // Invoke the Destination !
        logger.debug("Receiving request on the thread ''{0}''", Thread.currentThread());

        Bus old = BusFactory.getThreadDefaultBus();
        try {

            Bus bus = destination.getBus();
            BusFactory.setThreadDefaultBus(bus);

            HttpServletRequest httpRequest = request.getAttribute(HttpServletRequest.class);
            HttpServletResponse httpResponse = request.getAttribute(HttpServletResponse.class);
            ServletContext servletContext = request.getAttribute(ServletContext.class);


            if ("GET".equals(httpRequest.getMethod())) {
                processGetMethod(bus, httpRequest, httpResponse);
            } else {
                // this is a POST
                destination.invoke(servletContext, httpRequest, httpResponse);
            }

        } catch (IOException ioe) {
            throw new WSException(ioe);
        } finally {
            BusFactory.setThreadDefaultBus(old);
        }
    }

    /**
     * Process the GET style method. This will serve ?WSDL, ?XSD queries.
     * @param bus the curent bus
     * @param request the HTTP request
     * @param response the HTTP response
     * @throws WSException if there is an Exception during WSDL manipulation (endpoint's URL + XSD/WSDL imports).
     */
    private void processGetMethod(Bus bus, HttpServletRequest request, HttpServletResponse response) throws WSException {
        EndpointInfo ei = this.destination.getEndpointInfo();
        if (null != request.getQueryString()
            && request.getQueryString().length() > 0
            && bus.getExtension(QueryHandlerRegistry.class) != null) {

            String ctxUri = request.getPathInfo();
            String address = request.getRequestURL().toString();
            String baseUri = address + "?" + request.getQueryString();
            ei.setAddress(address);


            for (QueryHandler qh : bus.getExtension(QueryHandlerRegistry.class).getHandlers()) {
                if (qh.isRecognizedQuery(baseUri, ctxUri, ei)) {

                    if (qh instanceof WSDLQueryHandler) {
                        String url = request.getRequestURL().toString();
                        qh = new JOnASWSDLQueryHandler(bus,
                                                       url,
                                                       ei,
                                                       container);
                    }

                    response.setContentType(qh.getResponseContentType(baseUri, ctxUri));
                    try {
                        OutputStream out = response.getOutputStream();
                        qh.writeResponse(baseUri, ctxUri, ei, out);
                        out.flush();
                        return;
                    } catch (Exception e) {
                        throw new WSException(e);
                    }
                }
            }
        }
    }

    /* (non-Javadoc)
    * @see org.ow2.jonas.ws.jaxws.IWebServiceEndpoint#start()
    */
    public void start() {
        logger.debug("Start CXFWSEndpoint[{0}]", endpoint.getEndpointName());
        endpoint.publish();
    }

    /* (non-Javadoc)
     * @see org.ow2.jonas.ws.jaxws.IWebServiceEndpoint#stop()
     */
    public void stop() {
        logger.debug("Stop CXFWSEndpoint[{0}]", endpoint.getEndpointName());
        endpoint.stop();
    }


    /**
     * Return the port info.
     * @see org.ow2.jonas.ws.jaxws.IWebServiceEndpoint#getPortMetaData()
     */
    public PortMetaData getPortMetaData() {
        return portMetaData;
    }

    /**
     * @return the destination associated to this endpoint
     */
    public JOnASDestination getDestination() {
        return this.destination;
    }

    /**
     * Prints info about the endPoint.
     */
    public void displayInfos() {
        String endpointURL = "not yet available";
        String name = endpoint.getImplementorClass().getName();
        if (portMetaData != null && portMetaData.getEndpointURL() != null) {
            endpointURL = portMetaData.getEndpointURL();
        }
        logger.info("{0} endpoint ''{1}'' available at ''{2}''", type, name, endpointURL);

        // TODO: add debug informations too
    }

}
