/**
 * 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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id: BusCreator.java 21001 2011-03-16 12:34:00Z sauthieg $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ws.cxf;

import org.apache.cxf.Bus;
import org.apache.cxf.BusException;
import org.apache.cxf.BusFactory;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.message.Message;
import org.apache.cxf.transport.DestinationFactory;
import org.apache.cxf.transport.DestinationFactoryManager;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Level;
import static org.ow2.jonas.lib.util.Log.setComponentLogLevel;
import org.ow2.jonas.ws.cxf.http.JOnASHTTPTransportFactory;
import org.ow2.jonas.ws.jaxws.IJAXWSService;
import org.ow2.util.execution.ExecutionResult;
import org.ow2.util.execution.IExecution;
import org.ow2.util.execution.helper.RunnableHelper;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

import java.util.ArrayList;
import java.util.List;
import java.io.PrintStream;
import java.io.ByteArrayOutputStream;

import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

/**
 * Manage all the code required to start a new CXF Bus.
 * @author Guillaume Sauthier
 */
public class BusCreator extends BusFactory {

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

    /**
     * JAX-WS Service (used to retrieve handlers).
     */
    private IJAXWSService service;


    /**
     * Initialize Internal CXF Bus.
     * @return a new fully initialized CXF Bus
     */
    @Override
    public Bus createBus() {

        // Create a new Bus
        // Creation mechanism use the TCCL to access default Bus configuration
        IExecution<Bus> createBusJob = new IExecution<Bus>() {

            public Bus execute() throws Exception {

                // Bus start/stop operations are extra verbose
                Level old = setComponentLogLevel("org.apache.cxf", BasicLevel.LEVEL_ERROR);
                Level old2 = setComponentLogLevel("org.springframework", BasicLevel.LEVEL_ERROR);

                Bus newBus;

                // TODO: Remove these lines when issue will be closed
                // Avoid to get e.printStackTrace from CatalogManager class by catching error stream
                PrintStream previousErrorStream = System.err;
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                PrintStream tempStream = new PrintStream(baos);
                System.setErr(tempStream);

                // Get the current associated Bus (if any, may be null)
                Bus oldThreadBus = BusFactory.getThreadDefaultBus(false);

                try {
                    newBus =  BusFactory.newInstance().createBus();

                    // Store the Endpoint Audit Handler
                    newBus.setProperty(BusCreator.class.getName() + ".ENDPOINT", service.getEndpointAuditHandler());
                } finally {

                    // Dissociate the Bus from the Thread
                    BusFactory.setThreadDefaultBus(oldThreadBus);

                    System.setErr(previousErrorStream);

                    // Check if there was an UnknownHostException
                    String errorMessage = baos.toString();
                    if (errorMessage != null && errorMessage.contains("java.net.UnknownHostException")) {
                        // debug level
                        logger.debug("Error while initializing the Bus : ''{0}'' ", errorMessage);
                    } else if (!"".equals(errorMessage)) {
                        // error level
                        logger.error("Error while initializing the Bus : ''{0}'' ", errorMessage);
                    }

                    // Reset log levels
                    setComponentLogLevel("org.apache.cxf", old);
                    setComponentLogLevel("org.springframework", old2);
                }

                return newBus;
            }

        };

        ExecutionResult<Bus> res = new RunnableHelper<Bus>().execute(Bus.class.getClassLoader(),
                                                                     createBusJob);

        if (res.hasException()) {
            logger.error("Cannot create Bus", res.getException());
        }

        Bus bus = res.getResult();

        try {
            createDestinationFactory(bus);
        } catch (BusException e) {
            logger.error("Cannot create destination factory", e);
        }

        return bus;

    }

    /**
     * Create the EasyBeans destination factory and register the transports.
     * @throws BusException if the factory is not created.
     * @param bus Bus to be configured
     */
    private void createDestinationFactory(final Bus bus) throws BusException {

        JOnASHTTPTransportFactory factory = new JOnASHTTPTransportFactory(bus);

        // transportIds done by Spring ?
        List<String> namespaces = new ArrayList<String>();
        // SOAP11 over HTTP
        namespaces.add("http://schemas.xmlsoap.org/wsdl/soap/http");
        namespaces.add("http://schemas.xmlsoap.org/soap/http");
        namespaces.add("http://schemas.xmlsoap.org/wsdl/http/");

        // SOAP12 over HTTP
        namespaces.add("http://www.w3.org/2003/05/soap/bindings/HTTP/");

        namespaces.add("http://cxf.apache.org/transports/http/configuration");
        namespaces.add("http://cxf.apache.org/bindings/xformat");
        factory.setTransportIds(namespaces);

        for (String ns : namespaces) {
            registerTransport(bus, factory, ns);
        }

    }

    /**
     * Utility method to register {@link org.apache.cxf.transport.DestinationFactory} under a namespace.
     * @param bus Bus to be configured
     * @param factory the {@link org.apache.cxf.transport.DestinationFactory} instance
     * @param namespace the key
     * @throws BusException if registration fails.
     */
    private void registerTransport(final Bus bus,
                                   final DestinationFactory factory,
                                   final String namespace) throws BusException {

        DestinationFactoryManager manager = bus.getExtension(DestinationFactoryManager.class);
        manager.registerDestinationFactory(namespace, factory);
    }

    /**
     * Inject the service reference.
     * @param service Jaxws service
     */
    public void bindJaxwsService(IJAXWSService service) {
        this.service = service;
    }
}
