/*
 * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,  
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package org.switchyard.as7.extension.ws;

import java.net.URL;
import java.util.Map;
import java.util.ServiceLoader;

import javax.xml.ws.WebServiceException;

import org.jboss.as.security.plugins.SecurityDomainContext;
import org.jboss.as.webservices.security.SecurityDomainContextAdaptor;
import org.jboss.logging.Logger;
import org.jboss.as.web.host.ServletBuilder;
import org.jboss.as.web.host.WebDeploymentBuilder;
import org.jboss.as.web.host.WebDeploymentController;
import org.jboss.wsf.spi.classloading.ClassLoaderProvider;
import org.jboss.wsf.spi.deployment.WSFServlet;
import org.jboss.wsf.spi.metadata.webservices.JBossWebservicesFactory;
import org.jboss.wsf.spi.metadata.webservices.JBossWebservicesMetaData;
import org.jboss.wsf.spi.metadata.webservices.WebservicesMetaData;
import org.jboss.wsf.spi.publish.Context;
import org.jboss.wsf.spi.publish.EndpointPublisher;
import org.jboss.wsf.spi.publish.EndpointPublisherFactory;
import org.switchyard.ServiceDomain;
import org.switchyard.as7.extension.WebResource;
import org.switchyard.as7.extension.deployment.SwitchYardDeployment;
import org.switchyard.common.type.Classes;
import org.switchyard.component.common.Endpoint;
import org.switchyard.component.soap.InboundHandler;
import org.switchyard.component.soap.WebServicePublishException;
import org.switchyard.component.soap.config.model.EndpointConfigModel;
import org.switchyard.component.soap.config.model.SOAPBindingModel;
import org.switchyard.component.soap.endpoint.BaseWebService;

/**
 * Wrapper for JBossWS endpoints.
 *
 * @author Magesh Kumar B <mageshbk@jboss.com> (C) 2012 Red Hat Inc.
 */
public class JBossWSEndpoint implements Endpoint {

    private static final Logger LOGGER = Logger.getLogger("org.switchyard");

    private static final String HOST = "default-host";
    private static final EndpointPublisherFactory FACTORY;

    private EndpointPublisher _publisher;
    private Context _context;

    private WebResource _resource;

    /**
     * Construct a JBossWS endpoint on default host.
     * @throws Exception If a publisher could not be created
     */
    public JBossWSEndpoint() throws Exception {
        _publisher = FACTORY.newEndpointPublisher(HOST);
    }

    /**
     * Construct a JBossWS endpoint on specified host.
     * @param host The host on which the pubhlisher should created
     * @throws Exception if a publisher could not be created
     */
    public JBossWSEndpoint(String host) throws Exception {
        _publisher = FACTORY.newEndpointPublisher(host);
    }

    /**
     * {@inheritDoc}
     */
    public void publish(ServiceDomain domain, String contextRoot, Map<String, String> urlPatternToClassNameMap, WebservicesMetaData wsMetadata, SOAPBindingModel bindingModel, InboundHandler handler) throws Exception {
        EndpointConfigModel epcModel = bindingModel.getEndpointConfig();
        JBossWebservicesMetaData jbwsMetadata = null;
        if (epcModel != null) {
            String configFile = epcModel.getConfigFile();
            if (configFile != null) {
                URL jbwsURL = Classes.getResource(configFile, getClass());
                try {
                    JBossWebservicesFactory factory = new JBossWebservicesFactory(jbwsURL);
                    jbwsMetadata = factory.load(jbwsURL);
                } catch (WebServiceException e) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.error("Unable to load jboss-webservices metadata", e);
                    }
                    jbwsMetadata = new JBossWebservicesMetaData(jbwsURL);
                    jbwsMetadata.setConfigFile(configFile);
                }
            }
            String configName = epcModel.getConfigName();
            if (configName != null) {
                if (jbwsMetadata == null) {
                    jbwsMetadata = new JBossWebservicesMetaData(null);
                }
                jbwsMetadata.setConfigName(configName);
            }
        }
        ClassLoader tccl = Classes.getTCCL();
        _context = _publisher.publish(contextRoot, tccl, urlPatternToClassNameMap, wsMetadata, jbwsMetadata);
        for (org.jboss.wsf.spi.deployment.Endpoint ep : _context.getEndpoints()) {
            BaseWebService wsProvider = (BaseWebService)ep.getInstanceProvider().getInstance(BaseWebService.class.getName()).getValue();
            wsProvider.setInvocationClassLoader(tccl);
            // Hook the handler
            wsProvider.setConsumer(handler);
            // Hook the interceptors
            Interceptors.addInterceptors(ep, bindingModel, tccl);
            // Set the security domain
            ep.setSecurityDomainContext(new SwitchYardSecurityDomainContext(
                            domain.getServiceSecurity(bindingModel.getService().getComponentService().getSecurity()).getSecurityDomain(),
                            ep.getSecurityDomainContext()));
        }
        WebResource resource = SwitchYardDeployment.getResource(domain, contextRoot);
        if (resource == null) {
            resource = new WebResource();
            resource.setStarted(true);
            WebDeploymentController handle = _context.getEndpoints().get(0).getService().getDeployment().getAttachment(WebDeploymentController.class);
            resource.setHandle(handle);
            WebDeploymentBuilder deployment = new WebDeploymentBuilder();
            deployment.setContextRoot(contextRoot);
            ServletBuilder servlet = new ServletBuilder();
            servlet.setServletClass(WSFServlet.class);
            deployment.addServlet(servlet);
            resource.setDeployment(deployment); // A dummy deployment builder
            SwitchYardDeployment.addResource(domain, resource);
        }
        setWebResource(resource);
    }

    /**
     * Stop and destroy context.
     */
    public void stopContext() {
        if (_context != null && _publisher != null) {
            try {
                //undeploy endpoints
                _publisher.destroy(_context);
            } catch (Exception e) {
                LOGGER.error(e);
            }
        }
    }

    /**
     * Gets the WebResource associated with this endpoint.
     * @return The WebResource
     */
    public WebResource getWebResource() {
        return _resource;
    }

    /**
     * Sets the WebResource associated with this endpoint.
     * @param resource The resource
     */
    public void setWebResource(WebResource resource) {
        _resource = resource;
    }

    @Override
    public void start() {
        _resource.start(this);
    }

    @Override
    public void stop() {
        // The stopcontext ensures that the context is destroyed, so no need to do this
        // _resource.stop(this);
        stopContext();
    }

    static {
        try {
            ClassLoader loader = ClassLoaderProvider.getDefaultProvider().getWebServiceSubsystemClassLoader();
            FACTORY = ServiceLoader.load(EndpointPublisherFactory.class, loader).iterator().next();
        } catch (Exception e) {
            throw new WebServicePublishException(e);
        }
    }
}
