Search code examples
javaweb-servicessoapjax-wsmtom

Can't get PDF from SOAP with MTOM using jax-ws


I'm trying to get hold of a PDF returned from a web service call. With SoapUI and other logging I can see that the PDF is attached in the reply. But my attempts to get hold of the PDF fails. The DataHandler for the PDF contains no data.

// Call prepared and executed. result is returned object filled from sub-system.
System.out.println("Some value = " + result.getSomeValue());

DataHandler dh = result.getAttachedPDF();
OutputStream fos=new FileOutputStream("/tmp/out.pdf");
dh.writeTo(fos);
fos.close();

So the file /tmp/out.pdf is empty. Writing the PDF using a buffer yields same result:

InputStream inputstream = dh.getInputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while( (bytesRead = inputstream.read(buffer)) != -1) {
    System.out.println("Read " + bytesRead);
    fos.write(buffer);
}
fos.close();

I've tried to explicit enable MTOM in my client three different ways:

// First enabling
port = service.getPort(new MTOMFeature(true));

// Second enabling
Map<String, Object> ctxt = bp.getRequestContext();
ctxt.put("com.sun.xml.internal.ws.transport.http.client.streaming.chunk.size", 8192);

// Third enabling.
SOAPBinding binding = (SOAPBinding) bp.getBinding();
if( binding.isMTOMEnabled()) {
    System.out.println("MTOM enabled");
} else {
    binding.setMTOMEnabled(true);        
    System.out.println("MTOM NOT enabled");
}

I get "MTOM enabled" whether I use way 1 or 2 or not.

I've run the call inside a debugger (netbeans) and examined the 'result' object. All fields for the DataHandler is empty or having the value 0.

I tried to import the wsdl-file into a project in Eclipse. I abandoned that road when Eclipse reported multiple errors inside the generated source. Instead I let SoapUI generate classes (wsimport and wsdl2java) and used that along with my code. I achieved same result, empty PDF.

Using a SOAPHandler to intercept the messages as suggested here: http://java.globinch.com/enterprise-java/web-services/jax-ws/logging-tracing-web-service-xml-request-response-jax-ws/

The intercepted message I've saved and stored to file and is able to save each part.

Not sure where to go from here. I do feel that there are some bits that I've missed.

This is the actual code public static void main(String[] args) throws Exception { SVVCommonHeaderType header = new SVVCommonHeaderType(); header.setSystemId(SYSTEM_ID); header.setBrukerId("users name"); header.setHendelsesId(UUID.randomUUID().toString());

    GetKontrollseddel gs = new GetKontrollseddel();
    gs.setKontrollseddelNr(new BigInteger("3000036367"));

    Kontrollseddel2 service = new Kontrollseddel2();
    Kontrollseddel2PortType port = service.getKontrollseddel2Port(new MTOMFeature(true));
    BindingProvider bp = (BindingProvider) port;

    Map<String, Object> ctxt = bp.getRequestContext();
    ctxt.put("com.sun.xml.internal.ws.transport.http.client.streaming.chunk.size", 8192);
    ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, ENDPOINT_ADDRESS);
    ctxt.put(BindingProvider.USERNAME_PROPERTY, username);
    ctxt.put(BindingProvider.PASSWORD_PROPERTY, password);

    SOAPBinding binding = (SOAPBinding) bp.getBinding();
    if( binding.isMTOMEnabled()) {
        System.out.println("MTOM enabled");
    } else {
        binding.setMTOMEnabled(true);        
        System.out.println("MTOM NOT enabled");
    }

    GetKontrollseddelResponse resp = port.getKontrollseddel(gs, header);
    DataHandler dh = resp.getKontrollseddel2().getKontrollseddelPDF();
    OutputStream fos=new FileOutputStream("/tmp/out.pdf");
    dh.writeTo(fos);

    // Alternative way to write, same result
    /*
    InputStream inputstream = dh.getInputStream();

    byte[] buffer = new byte[1024];
    int bytesRead;
    while( (bytesRead = inputstream.read(buffer)) != -1) {
        System.out.println("Read " + bytesRead);
        fos.write(buffer);
    }
    */

    fos.close();
}

Edit: As a workaround I've added code in the logging class that extracts the PDF. The handleMessage() looks like

@Override
public boolean handleMessage(SOAPMessageContext arg0) {
    SOAPMessage message= arg0.getMessage();
    boolean isOutboundMessage=  (Boolean)arg0.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);

    if(!isOutboundMessage){
        Iterator iter = message.getAttachments();
        while( iter.hasNext()) {
            AttachmentPart part = (AttachmentPart) iter.next();
            try {
                attachmentInputstream = (InputStream) part.getContent();
            } catch(Exception ex) {
                System.out.println("Exception in handling attachment");
                ex.printStackTrace(System.out);
                attachmentInputstream = null;
            }
        }
    }

    return true;
}

If this workaround is to be used, I've got to add some additional handling code. I'll decide later.


Solution

  • It turned out that the reason for the problem was that there were some firewall at senders side the did some rewriting of the SOAP attachment. This confused jax-ws while SoapUI processed the message anyway.

    The answer I got from the producer of the message was that they had a proxy that turned the header from XOP to text while MTOM need to be XOP.

    With this in place, my code worked flawless.