I am working on Streaming Attachment with SOAP webservices these days, because I need to send GBs of data in one SOAP invocation. Naturally MTOM/XOP is required so base64Binary can become MIME attachment. Everything works well with both client and server. Streaming GB of binary via the SOAP call was a breeze and memory usage on both Client and Server are kept in minimum. The problem arose when I had to add WSSE with UserNameToken/Password into the SOAP header. The way you would add WSSE header is via Handler. The handler itself works fine and user/password are in the WSSE header and taken nicely by the server. The problem is the XOP/MTOM that had been working just disappeared! The base64Binary went back into SOAP as an inline data inside the XML as base64 strings. As a result, the Streaming functionality is now out of the question. A couple of hundred MB of payload will yield out of memory exception!
The reason of this happening is basically this. The Handler that adds WSSE header is doing its job AFTER the MTOM/XOP is being processed. When the Handler is processing the SOAP message, it will de-XOP it and push binary payload back to the SOAP before adding the WSSE header. As a result, MTOM is gone and if the payload is too big, out of memory exception happens.
Googling shows there are many others having the same problem. In particular, To shreds explained similar thing and offers a work around solution. The work around basically manually implements the XOP process and pushes binary from SOAP inline content back to XOP attachment. I actually tried to implement that work around. It works but it still has a problem. The processing of move the binary back to XOP requires to load the complete payload content in memory first. That requires again a lot of memory. It is true that the transporting from client to server is back to MTOM. But it kind of defeat the purpose for me because I intended to stream GBs of data without loading whole payload into memory.
Finally, my attention diverted from JAX_WS default implementation to CXF. CXF’s factory model allows the client to add WSSE header first before invoking service. Hence, XOP/MTOM will be intact since that is the last step before leaving for the server’s endpoint.
Here is my working code:
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
Map<String,Object> props = new HashMap<String, Object>();
// ensure SOAP 1.2
SoapBindingConfiguration configuration = new SoapBindingConfiguration();
Map <String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.MUST_UNDERSTAND, “0”); // false/0 or true/1
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
// Monitors both direction
// Custom Service Class XYZ
XYZ client = (XYZ) factory.create();
MyFileUploader f = new MyFileUploader();
DataHandler dh = new DataHandler(new FileDataSource(“c:\10GB.zip”));
XYZResponse nr = client.streamingUpload(f);
With the above code, you will have both WSSE UserNameToken and Streaming MTOM in SOAP calls. The Streaming works exceptionally well with almost a flat liner in memory usage.
I haven’t tried other WSSE functions such as signing/encryption. Hope I can get to them later this week if needed.
Be good and write good code!