/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 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: JOnASJaxWsServerFactoryBean.java 21306 2011-05-24 15:44:09Z benoitf $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ws.cxf;

import java.util.ArrayList;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.PortInfo;

import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import org.apache.cxf.jaxws.support.JaxWsEndpointImpl;
import org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean;
import org.ow2.jonas.ws.jaxws.handler.PostConstructProcessor;
import org.ow2.jonas.ws.jaxws.handler.builder.AnnotationHandlerChainBuilder;
import org.ow2.jonas.ws.jaxws.handler.builder.HandlerChainBuilder;
import org.ow2.util.annotation.processor.DefaultAnnotationProcessor;
import org.ow2.util.annotation.processor.IAnnotationProcessor;
import org.ow2.util.annotation.processor.ProcessorException;

/**
 * The JOnASJaxwsServerFactoryBean is responsible to build the handler chain
 * associated with the endpoint.
 *
 * @author Guillaume Sauthier
 */
public class JOnASJaxWsServerFactoryBean extends JaxWsServerFactoryBean {

    /**
     * Handler Chain Builder.
     */
    private HandlerChainBuilder handlerChainBuilder;

    /**
     * Constructs a new ServerFactory using the given ServiceFactory.
     * @param serviceFactory wrapped ServiceFactory
     */
    public JOnASJaxWsServerFactoryBean(final JaxWsServiceFactoryBean serviceFactory) {
        super(serviceFactory);

        // Disengage Handler's building in CXF
        this.doInit = false;
    }

    public void setHandlerChainBuilder(final HandlerChainBuilder builder) {
        this.handlerChainBuilder = builder;
    }

    @Override
    public Server create() {
        // Override classloader as SoapBindingUtil will use Thread Context
        // classloader in order to create proxies.
        // We're setting Context classloader to CXF classloader
        // http://jira.ow2.org/browse/JONAS-369
        ClassLoader old = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(JaxWsServiceFactoryBean.class.getClassLoader());
            Server server = super.create();

            // Init the handlers with our own builders
            buildHandlerChain();

            return server;
        } finally {
            Thread.currentThread().setContextClassLoader(old);
        }

    }

    /**
     * Build the handler chain.
     */
    private void buildHandlerChain() {

        // Init the builder if required
        if (handlerChainBuilder == null) {
            // By default, use a annotation based builder
            this.handlerChainBuilder = new AnnotationHandlerChainBuilder(getServiceBeanClass());
        }

        // Identify the target port
        final JaxWsServiceFactoryBean sf = (JaxWsServiceFactoryBean) getServiceFactory();
        PortInfo portInfo = new PortInfo() {

            public QName getServiceName() {
                return sf.getServiceQName();
            }

            public QName getPortName() {
                return sf.getEndpointInfo().getName();
            }

            public String getBindingID() {
                return getBindingId();
            }
        };

        // Build the chain using annotations
        List<Handler> chain = new ArrayList<Handler>(handlers);

        // Add the Handlers in the List
        chain.addAll(handlerChainBuilder.buildHandlerChain(portInfo, getHandlerClassLoader()));

        // Init the Handlers
        for (Handler h : chain) {
            // Inject resources
            injectHandler(h);
            // Call @PostConstruct
            constructHandler(h);
        }

        // Assign them to the Binding
        ((JaxWsEndpointImpl) getServer().getEndpoint()).getJaxwsBinding().setHandlerChain(chain);

    }

    /**
     * @return the classloader to be used for Handler loading
     */
    protected ClassLoader getHandlerClassLoader() {
        return this.getServiceBeanClass().getClassLoader();
    }

    /**
     * Manage handler resource injection.
     * @param handler injected object.
     */
    private void injectHandler(final Handler handler) {
        IAnnotationProcessor processor = new DefaultAnnotationProcessor();
        // TODO inject other references (WebServiceContext, ...)
        //processor.addAnnotationHandler(new CXFWebServiceContextResourceProcessor());

        try {
            processor.process(handler);
        } catch (ProcessorException e) {
            throw new WebServiceException("Cannot inject resources", e);
        }
    }

    /**
     * Manage handler lifecycle.
     * @param handler object to be started
     */
    private void constructHandler(final Handler handler) {
        IAnnotationProcessor processor = new DefaultAnnotationProcessor();
        processor.addAnnotationHandler(new PostConstructProcessor());

        try {
            processor.process(handler);
        } catch (ProcessorException e) {
            throw new WebServiceException("Cannot start handler", e);
        }
    }
}
