> I am finding that my attempts at writing a custom SOAP Transport are
> failing as well.. so perhaps you meant the former. This is
> frustrating..

Attached is HttpClientConnection.java, which is my implementation of an
HTTP/1.1 Apache SOAP 2.x transport using the HTTPClient package from
<http://www.innovation.ch/java/HTTPClient/>.  HTTPClient doesn't directly
support SSL, which you were asking about, though the web site does point
to some patches to provide SSL.  I haven't analyzed what work would be
needed in the attached file to use SSL instead of plain HTTP.
I developed this for something a while back and ended up ultimately not
using it, so it sort of stands on its own now.  I know of no
problems/issues with it except as mentioned in comments within.
-- 
[EMAIL PROTECTED] (WJCarpenter)    PGP 0x91865119
38 95 1B 69 C9 C6 3D 25    73 46 32 04 69 D6 ED F3


 // $Revision:   1.3  $
 // $Date:   16 Sep 2002 17:55:58  $
 // $Workfile:   HttpClientConnection.java  $

package org.carpenter.bill;  // your package name here

import java.io.*;
import java.net.*;
import java.util.*;

import HTTPClient.*;
import org.apache.soap.*;
import org.apache.soap.encoding.*;
import org.apache.soap.encoding.soapenc.*;  // for Base64
import org.apache.soap.rpc.*;
import org.apache.soap.transport.*;
import org.apache.soap.util.net.*;

/**
 * Use HTTPClient to implement persistent connections for use with Apache SOAP 2.x.  
You can
 * find HTTPClient here:  <http://www.innovation.ch/java/HTTPClient/>.  License is GNU 
LGPL.
 *
 * This class implements SOAPTransport so that Apache SOAP will use it as such.  
However,
 * there are some internal places that Apache SOAP asks if the SOAPTranposrt its been 
given
 * is an instanceof SOAPHTTPConnection.  So, this class extends that but overrides all 
the
 * methods, calling instead things belonging to HTTPClient.  Were it not for that, we 
could
 * just subclass java.lang.Object instead, which would be better.
 */
public class HttpClientConnection extends 
org.apache.soap.transport.http.SOAPHTTPConnection implements SOAPTransport
{
    // SOAPTransport
    public void send(URL currentUrl, String action, Hashtable headers, Envelope env, 
SOAPMappingRegistry smr, SOAPContext requestContext) throws SOAPException
    {
        if (headers == null)
        {
            headers = new Hashtable();
        }
        if (needNewConnection(currentUrl))
        {
            // Usually we expect all requests to go to the same host.  If they don't, 
this
            // loses and we don't get HTTP/1.1 reuse.  C'est la vie.  Still works, 
though; just
            // more slowly.
            try
            {
                httpConnection = new HTTPConnection(currentUrl);
            }
            catch (ProtocolNotSuppException p)
            {
                throw new SOAPException(Constants.FAULT_CODE_CLIENT, "Unable to create 
new HTTPConnection for " + currentUrl.toString() + ".", p);
            }
        }
        if (proxyHost != null)
        {
            httpConnection.setProxyServer(proxyHost, proxyPort);
        }
        if (userName != null)
        {
            httpConnection.addBasicAuthorization(null, userName, password);
        }
        if (proxyUserName != null)
        {
            // add the Proxy-Authorization header for proxy authentication
            headers.put(Constants.HEADER_PROXY_AUTHORIZATION, "Basic " + 
encodeAuth(proxyUserName, proxyPassword));
        }
        if (timeout > 0)
        {
            httpConnection.setTimeout(timeout);
        }
        previousUrl = currentUrl;
        responseSoapContext = null;
        responseReader = null;
        responseHeaders = null;
        String payload = null;
        if (env != null)
        {
            StringWriter payloadSW = new StringWriter();
            try
            {
                env.marshall(payloadSW, smr, requestContext);
            }
            catch (IOException e)
            {
                throw new SOAPException(Constants.FAULT_CODE_CLIENT, "Unable to 
marshall the SOAP envelope.", e);
            }
            payload = payloadSW.toString();
        }

        try
        {
            // a lot of work just to get the content-type...
            TransportMessage requestTMessage = new TransportMessage(payload, 
requestContext, new Hashtable());
            requestTMessage.save();
            headers.put(Constants.HEADER_CONTENT_TYPE, 
requestTMessage.getContentType());
        }
        catch (IOException e)
        {
            throw new SOAPException(Constants.FAULT_CODE_CLIENT, "Unable to process 
the request SOAP envelope.", e);
        }
        catch (javax.mail.MessagingException e)
        {
            throw new SOAPException(Constants.FAULT_CODE_CLIENT, "Unable to process 
the request SOAP envelope.", e);
        }

        headers.put(Constants.HEADER_SOAP_ACTION, (action != null) ? ('\"' + action + 
'\"') : "");

        NVPair[] nvheaders = new NVPair[headers.size()];
        Iterator it = headers.entrySet().iterator();
        for (int ndex=0; it.hasNext(); ++ndex)
        {
            Map.Entry me = (Map.Entry)it.next();
            String name  = (String)me.getKey();
            String value = (String)me.getValue();
            nvheaders[ndex] = new NVPair(name, value);
        }
        HTTPResponse httpResponse = null;
        try
        {
            // We could just Post(...payloadBytes...), but then HTTPClient does
            // chunked encoding.  Apache httpd loses its lunch over chunked encoding
            // in requests.
            byte[] payloadBytes = payload.getBytes();
            HttpOutputStream hos = new HttpOutputStream(payloadBytes.length);
            httpResponse = httpConnection.Post(currentUrl.getPath(), hos, nvheaders);
            hos.write(payloadBytes);
            hos.close();
        }
        catch (ModuleException e)
        {
            /** @todo is this the right kind of fault code? */
            throw new SOAPException(Constants.FAULT_CODE_PROTOCOL, "Unable to POST 
request to " + currentUrl.getPath() + ".", e);
        }
        catch (IOException e)
        {
            /** @todo is this the right kind of fault code? */
            throw new SOAPException(Constants.FAULT_CODE_PROTOCOL, "Unable to POST 
request to " + currentUrl.getPath() + ".", e);
        }

        responseSoapContext = new SOAPContext();
        try
        {
            Enumeration responseHdrs = httpResponse.listHeaders();
            responseHeaders = new Hashtable();
            while (responseHdrs.hasMoreElements())
            {
                String key = (String)responseHdrs.nextElement();
                String value = httpResponse.getHeader(key);
                responseHeaders.put(key, value);
            }
            byte[] data = httpResponse.getData();
            int cl = data.length;
            String ct = httpResponse.getHeader("content-type");
            TransportMessage responseTMessage = new TransportMessage(new 
ByteArrayInputStream(data), cl, ct, responseSoapContext, responseHeaders);
            responseTMessage.read();
            Reader envReader = responseTMessage.getEnvelopeReader();
            if (envReader != null)
            {
                responseReader = new BufferedReader(envReader);
            }
            else
            {
                responseReader = null;
            }
        }
        catch (ModuleException e)
        {
            /** @todo is this the right kind of fault code? */
            throw new SOAPException(Constants.FAULT_CODE_PROTOCOL, "Unable to 
read/parse HTTP response.", e);
        }
        catch (IOException e)
        {
            /** @todo is this the right kind of fault code? */
            throw new SOAPException(Constants.FAULT_CODE_PROTOCOL, "Unable to 
read/parse HTTP response.", e);
        }
        catch (javax.mail.MessagingException e)
        {
            /** @todo is this the right kind of fault code? */
            throw new SOAPException(Constants.FAULT_CODE_PROTOCOL, "Unable to 
read/parse HTTP response.", e);
        }
    }
    // SOAPTransport
    public BufferedReader receive()
    {
        return responseReader;
    }
    // SOAPTransport
    public SOAPContext getResponseSOAPContext()
    {
        return responseSoapContext;
    }
    // SOAPTransport
    public Hashtable getHeaders()
    {
        return responseHeaders;
    }
    public void setProxyHost(String proxyHost)
    {
        /** @todo WJC:  I didn't test this feature */
        this.proxyHost = proxyHost;
    }
    public void setProxyPort(int proxyPort)
    {
        /** @todo WJC:  I didn't test this feature */
        this.proxyPort = proxyPort;
    }
    public String getProxyHost()
    {
        return proxyHost;
    }
    public int getProxyPort()
    {
        return proxyPort;
    }
    public void setUserName(String userName)
    {
        /** @todo WJC:  I didn't test this feature */
        this.userName = userName;
    }
    public void setPassword(String password)
    {
        /** @todo WJC:  I didn't test this feature */
        this.password = password;
    }
    public void setProxyUserName(String proxyUserName)
    {
        /** @todo WJC:  I didn't test this feature */
        this.proxyUserName = proxyUserName;
    }
    public void setProxyPassword(String proxyPassword)
    {
        /** @todo WJC:  I didn't test this feature */
        this.proxyPassword = proxyPassword;
    }
    public void setMaintainSession(boolean maintainSession)
    {
        /** @todo WJC:  I didn't test this feature */
        // Just always accept them.  What's the big deal?
        return;
    }
    public boolean getMaintainSession()
    {
        /** @todo WJC:  I didn't test this feature */
        // hardwired and can't be turned off (well, HTTPClient supports it, but I'm 
too lazy)
        return true;
    }
    public void setTimeout(int timeout)
    {
        /** @todo WJC:  I didn't test this feature */
        this.timeout = timeout;
    }
    public int getTimeout()
    {
        /** @todo WJC:  I didn't test this feature */
        return timeout;
    }
    public void setOutputBufferSize(int outputBufferSize)
    {
        /** @todo WJC:  I didn't test this feature */
        /** @todo does nothing */
        this.outputBufferSize = outputBufferSize;
    }
    public int getOutputBufferSize()
    {
        /** @todo WJC:  I didn't test this feature */
        /** @todo does nothing */
        return outputBufferSize;
    }
    public void setTcpNoDelay(Boolean tcpNoDelay)
    {
        /** @todo WJC:  I didn't test this feature */
        // HTTPClient doesn't provide an API for this.  In fact, it doesn't do anything
        // about this setting on the socket, so it takes the default of having the 
Nagle
        // algorithm enabled.  Since that is always a mistake in our context, you might
        // like to hardwire in a call to sock.setTcpNoDelay(true) right before the 
return
        // in HTTPConnection.getSocket.
        this.tcpNoDelay = tcpNoDelay;
    }
    public Boolean getTcpNoDelay()
    {
        /** @todo WJC:  I didn't test this feature */
        // no API for this in HTTPClient
        return tcpNoDelay;
    }
    private boolean needNewConnection(URL currentUrl)
    {
        if (httpConnection == null) return true;
        if (previousUrl == null) return true;
        String oldHost = previousUrl.getHost();
        if (oldHost == null) return true;
        String newHost = currentUrl.getHost();
        if (!oldHost.equalsIgnoreCase(newHost)) return true;
        int oldPort = previousUrl.getPort();
        int newPort = currentUrl.getPort();
        if (oldPort != newPort) return true;
        /** @todo WJC: more checks for reusability of the connection ... maybe check 
proxies and protocol */
        return false;
    }
    // copied from SOAPHTTConnection, where it's also private
    private static String encodeAuth(String userName, String password) throws 
SOAPException
    {
        try
        {
            return Base64.encode((userName + ":" + password).getBytes("8859_1"));
        }
        catch (UnsupportedEncodingException e)
        {
            throw new SOAPException (Constants.FAULT_CODE_CLIENT, e.getMessage(), e);
        }
    }


    private SOAPContext responseSoapContext = null;
    private HTTPConnection httpConnection = null;
    private URL previousUrl = null;
    private BufferedReader responseReader = null;
    private Hashtable responseHeaders = null;
    private String proxyHost = null;
    private int proxyPort = 80;
    private String userName = null;
    private String password = null;
    private String proxyUserName = null;
    private String proxyPassword = null;
    private int timeout = 0;
    private int outputBufferSize = HTTPUtils.DEFAULT_OUTPUT_BUFFER_SIZE; /* not really 
used */
    Boolean tcpNoDelay = null; /* not really used */
}

Reply via email to