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