/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.remoting.transport.soap.axis;

import org.jboss.axis.AxisFault;
import org.jboss.axis.client.Call;
import org.jboss.axis.client.Service;
import org.jboss.axis.message.SOAPBodyElementImpl;
import org.jboss.axis.transport.http.SimpleAxisServer;
import org.jboss.axis.utils.Messages;
import org.jboss.remoting.InvokerLocator;
import org.jboss.remoting.ServerInvoker;
import org.jboss.remoting.transport.PortUtil;

import javax.xml.rpc.ServiceException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.URL;
import java.net.UnknownHostException;
import java.rmi.RemoteException;
import java.util.Vector;

/**
 * SOAPServerInvoker is the Server-side of a SOAP transport
 *
 * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
 * @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
 * @version $Revision: 1.6.2.3 $
 */
public class SOAPServerInvoker extends ServerInvoker
{
    private SimpleAxisServer axis;

    public SOAPServerInvoker (InvokerLocator locator)
    {
        super (locator );
    }

    /**
     * subclasses should override to provide any specific start logic
     *
     * @throws IOException
     */
    public void start () throws IOException
    {
        super.start ();
        int port = locator.getPort();
        if (port<=0)
        {
            port = PortUtil.findFreePort();
            this.locator = new InvokerLocator(locator.getProtocol(), locator.getHost(), port, locator.getPath(), locator.getParameters());
        }
        this.axis = new SimpleAxisServer();
        this.axis.setServerSocket(new ServerSocket(port));
        try
        {
            this.axis.start();
            deployService();
            URL serviceURL = new URL("http://"+locator.getHost()+":"+port+"/axis/services/AxisSOAPConnector");
            AxisSOAPConnectorService service = new AxisSOAPConnectorServiceLocator();
            AxisSOAPConnector connector = service.getAxisSOAPConnector(serviceURL);
            ((AxisSOAPConnectorSoapBindingStub) connector).setTimeout(60000);
            for (int c=0;c<3;c++)
            {
                try
                {
                    System.err.println("calling initialize: "+locator.getLocatorURI());
                    connector.initialize(locator.getLocatorURI());
                }
                catch (Throwable cn)
                {
                    // ignore -- seems to sometimes take some time for the deploy to be registerd
                    // and seems to be async from when we call
                    Thread.currentThread().sleep(1000);
                }
            }
        }
        catch (Exception ex)
        {
            throw new IOException(ex.getMessage());
        }
    }

    /**
     * subclasses should override to provide any specific stop logic
     */
    public void stop ()
    {
        try
        {

            unDeployService();
        }
        catch (Exception ex) {}
        try
        {
            this.axis.stop();
        }
        catch (Exception ex) {}
        super.stop ();
    }

    /**
     * Have to use this service name when starting internall since the path is different
     * than if accessing externally (see service vs services in paths).
     * @return String representing internal service path
     */
    private String getPrivateServiceName()
    {
        return "/axis/service/AxisSOAPConnector";
    }

    private String getPublicServiceName()
    {
        return "/axis/services/AxisSOAPConnector";
    }
    public URL getServiceURL() throws MalformedURLException
    {
        return createServiceURL(getPublicServiceName());
    }
    private URL createServiceURL(String serviceName) throws MalformedURLException
    {
        String localhost = null;
        URL serviceURL = null;
        try
        {
            localhost = "http://" + InetAddress.getLocalHost().getHostAddress() + ":" + locator.getPort() + serviceName;
        }
        catch(UnknownHostException ex)
        {
            localhost = "http://localhost:" + locator.getPort() + serviceName;
        }
        serviceURL = new URL(localhost);
        log.debug("server creating locatorURI => "+serviceURL);
        return serviceURL;
    }

    /**
     * returns true if the transport is bi-directional in nature, for example,
     * SOAP in unidirectional and SOCKETs are bi-directional (unless behind a firewall
     * for example).
     *
     * @return
     */
    public boolean isTransportBiDirectional ()
    {
        return false;
    }
/**
     * This will create a server-config.wsdd in the working directory
     *
     * @throws ServiceException
     * @throws MalformedURLException
     * @throws RemoteException
     */
    private void deployService()
            throws ServiceException, MalformedURLException, RemoteException
    {
        InputStream xmlDeploymentStream = getServiceDeployment();
        manageService(xmlDeploymentStream);
    }

    private void unDeployService()
            throws ServiceException, MalformedURLException, RemoteException
    {
        InputStream xmlDeploymentStream = getServiceUnDeployment();
        manageService(xmlDeploymentStream);
    }

    /**
     * Used to deploy or undeploy service based on deployment stream.  Done by making
     * SOAP calls to AdminService.
     *
     * @param xmlDeploymentStream
     * @throws ServiceException
     * @throws MalformedURLException
     * @throws RemoteException
     */
    private void manageService(InputStream xmlDeploymentStream)
            throws ServiceException, MalformedURLException, RemoteException
    {
        Service service = new Service();
        Call call = (Call) service.createCall();

        setupCall(call);

        call.setUseSOAPAction(true);
        call.setSOAPActionURI("AdminService");

       // @todo fix this ctor by transforming xmlDeploymentStream to correct type
        Object[] params = new Object[]{new SOAPBodyElementImpl()};
        Vector result = (Vector) call.invoke(params);

        if(result == null || result.isEmpty())
        {
            throw new AxisFault(Messages.getMessage("nullResponse00"));
        }

        //SOAPBodyElement body = (SOAPBodyElement) result.elementAt(0);
    }

    private void setupCall(Call call) throws MalformedURLException
    {
        call.setTargetEndpointAddress(createServiceURL(getPrivateServiceName()));
    }

    private InputStream getServiceDeployment()
    {
        String deploymentString = getDeploymentString();
        return createInputStream(deploymentString);
    }

    private InputStream createInputStream(String deploymentString)
    {
        return new ByteArrayInputStream(deploymentString.getBytes());
    }

    private String getDeploymentString()
    {
        String deploymentXML = "<deployment xmlns=\"http://xml.apache.org/axis/wsdd/\"" +
                " xmlns:java=\"http://xml.apache.org/axis/wsdd/providers/java\">" +
                " <service name=\"AxisSOAPConnector\" provider=\"java:RPC\" style=\"rpc\" use=\"encoded\">" +
                " <parameter name=\"wsdlTargetNamespace\" value=\"urn:AxisSOAPConnector\"/>" +
                " <parameter name=\"wsdlServiceElement\" value=\"AxisSOAPConnectorService\"/>" +
                " <parameter name=\"wsdlServicePort\" value=\"AxisSOAPConnector\"/>" +
                " <parameter name=\"className\" value=\""+AxisSOAPConnectorSoapBindingSkeleton.class.getName()+"\"/>" +
                " <parameter name=\"wsdlPortType\" value=\"AxisSOAPConnector\"/>" +
                " <parameter name=\"allowedMethods\" value=\"*\"/>" +
                " <parameter name=\"scope\" value=\"Application\"/>" +
                " </service>" +
                " </deployment>";

        return deploymentXML;
    }

    private InputStream getServiceUnDeployment()
    {
        String undeploymentString = getUnDeploymentString();
        return createInputStream(undeploymentString);
    }


    private String getUnDeploymentString()
    {
        String undeploymentXML = "<undeployment xmlns=\"http://xml.apache.org/axis/wsdd/\">" +
                "<service name=\"AxisSOAPConnector\"/>" +
                "</undeployment>";
        return undeploymentXML;
    }
}
