/**
 * 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$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ws.jaxws.handler;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;

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;

public class JOnASHandlerResolver implements HandlerResolver {

    /**
     * The application classloader (used to load handler classes)
     */
    private ClassLoader applicationClassLoader;

    /**
     * Map, for each port, the List oh Handlers to use.
     */
    private final Map<PortInfo, List<Handler>> handlerMap = new HashMap<PortInfo, List<Handler>>();

    /**
     * Which builder will be used ?
     */
    private HandlerChainBuilder handlerChainBuilder;

    /**
     *  Constructs a new HandlerResolver for the given Class
     * @param clazz supported class
     * @param appsClassLoader Handler's classloader
     */
    public JOnASHandlerResolver(final Class<?> clazz, final ClassLoader appsClassLoader) {
        this.applicationClassLoader = appsClassLoader;

        // By default, provide an annotation based builder
        this.handlerChainBuilder = new AnnotationHandlerChainBuilder(clazz);
    }

    /**
     * @param handlerChainBuilder the Builder to use
     */
    public void setHandlerChainBuilder(final HandlerChainBuilder handlerChainBuilder) {
        this.handlerChainBuilder = handlerChainBuilder;
    }

    /**
     * Gte the handlers chain associated with the given port.
     * @param portInfo Port identifier (service name, port name and protocol binding ID)
     * @return the list of Handlers
     */
    public List<Handler> getHandlerChain(final PortInfo portInfo) {

        List<Handler> handlerChain = handlerMap.get(portInfo);
        if (handlerChain == null) {
            handlerChain = createHandlerChain(portInfo);
            handlerMap.put(portInfo, handlerChain);
        }
        return handlerChain;
    }

    /**
     * Creates a Handler chain for the given port.
     * @param portInfo port identifier
     * @return a new handler chain for the given port
     */
    private List<Handler> createHandlerChain(final PortInfo portInfo) {
        List<Handler> chain = handlerChainBuilder.buildHandlerChain(portInfo,
                                                           applicationClassLoader);

        // TODO improve @Resource handling
        for (Handler h : chain) {
            configHandler(h);
            callPostConstructs(h);
        }

        return chain;
    }

    /**
     * Start the given Handler by calling all its @postConstruct methods.
     * @param handler handler to be started
     */
    private void callPostConstructs(final Handler handler) {

        IAnnotationProcessor processor = new DefaultAnnotationProcessor();
        processor.addAnnotationHandler(new PostConstructProcessor());

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

    /**
     * JAX-WS section 9.3.1: The runtime MUST then carry out any injections
     * requested by the handler, typically via the javax .annotation.Resource
     * annotation. After all the injections have been carried out, including in
     * the case where no injections were requested, the runtime MUST invoke the
     * method carrying a javax.annotation .PostConstruct annotation, if present.
     */
    private void configHandler(final Handler handler) {
        if (handler != null) {/*
            ResourceManager resourceManager = bus.getExtension(ResourceManager.class);
            List<ResourceResolver> resolvers = resourceManager.getResourceResolvers();
            resourceManager = new DefaultResourceManager(resolvers);
            ResourceInjector injector = new ResourceInjector(resourceManager);
            injector.inject(handler);
            injector.construct(handler);*/
        }

    }
}
