Search code examples
tomcatsoapjax-wsout-of-memorymtom

JAX-WS: OutOfMemory with large attachments + ChainHandler


My problem is similar to: JAX-WS SoapHandler with large messages: OutOfMemoryError and JAXWS Soap Handler Large MTOM Attachments

I am using tomcat, the Metro-Runtime-Configuration, and when I want to add a header in my HandlerChain it gives me a OutOfMemory exception.

public boolean handleMessage(SOAPMessageContext smc) {
    if (Boolean.TRUE.equals(smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY))) {
        SOAPMessage sm = smc.getMessage(); // <- OutOfMemory
        ...

In the first link Ahmed writes that I was able to write code that handles the raw data stream in 3 out of the 4 cases. Fortunately the three cases included the two we were mostly interested in: to/from the server. but he doesn't provide a code snippet for that.

Things I tried:

The OutOfMemory occurs in JDK7 AbstractMessageImpl#readAsSOAPMessage which I wasn't able to override. Is it possible to override that class using a custom bootstrap? And how to do so? I was able to "override" the Integer-class, but the AbstractMessageImpl was not taken.

Changing the runtime to Apache CXF did not work with my generated JAX-WS RI Clients (Cannot create a secure XMLInputFactory)

Is there another possibility to use another provider/runtime? And if not, how can I override JDK7's AbstractMessageImpl#readAsSOAPMessage?

OR: is it possible to configure the order of MTOM and the handler chains? A MTOM'ed message shouldn't throw a OutOfMemory, is that right?

Thanks in advance crappi


Solution

  • This problem is also found in the ticket reports here: https://java.net/jira/browse/WSIT-1081

    I solved this problem by not using a handler but adding a header when creating a port like this:

    Web-Service-Creation

    WebServiceEndpoint endpoint = w.getWebServiceEndpointPort(new MTOMFeature());
    Map<String, Object> ctxt = ((BindingProvider) endpoint).getRequestContext();
    // Enable HTTP chunking mode, otherwise HttpURLConnection buffers
    ctxt.put("com.sun.xml.ws.transport.http.client.streaming.chunk.size", 8192);
    WSBindingProvider bp = (WSBindingProvider) endpoint;
    bp.setOutboundHeaders(Headers.create(JAXBContext.newInstance(WSSecurityHeader.class), 
                          new WSSecurityHeader()));
    

    Since Header-creation is not provided with methods, JAXB is used for complex header types.

    WSSecurityHeader.java

    @XmlRootElement(name = "Security")
    public class WSSecurityHeader {
    
        @XmlElement(name = "UsernameToken")
        public WSSecurityUsernameToken usernameToken = new WSSecurityUsernameToken();
        ...
    }
    

    Disadvantages

    The client needs many libraries that this code is working: JAXB, JAX-WS, StreamBuffer, Policy, Stax-Ex, gmbal-api-only, which is horrible for just adding a header. (Especially if you are working with a fat client without maven support)