How to get the XML response body back from a JAX-WS client?

user1955401 picture user1955401 · May 20, 2013 · Viewed 9.8k times · Source

I've found a read a number of threads on here about how to retrieve the XML response from a JAX-WS client. In my case, the client is generated from the WSDL via Oracle's JDeveloper product and is going to invoke a Document/Literal service endpoint that was written in .NET. What I want to do is obtain the XML response from the call FROM the calling client, not from inside a handler.

The closest thread that I saw to this issue was: http://www.coderanch.com/t/453537/Web-Services/java/capture-SoapRequest-xml-SoapResponse-xml

I don't think I want to generate a Dispatch call because the endpoint's XML schema for the SOAP packet is rather complex, and the automatic proxy makes the call trivial. Unless there is some other way to populate the generated bean(s) and then invoke some method that simply produces the XML and I then make the call?

private void storeSOAPMessageXml(SOAPMessageContext messageContext) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    SOAPMessage soapMessage = messageContext.getMessage();
    try {
        soapMessage.writeTo(baos);
        String responseXml = baos.toString();
        log.debug("Response: " + responseXml );
        PaymentGatewayXMLThreadLocal.set(responseXml);
    } catch (SOAPException e) {
        log.error("Unable to retrieve SOAP Response message.", e);
    } catch (IOException e) {
        log.error("Unable to retrieve SOAP Response message.", e);
    }
}

My thought was to store the response to the call in a ThreadLocal inside the handler and then read it after the call. Is that reasonable? So after the handler does the above code in the handleMessage and handleFault, the client calling code invokes this method:

@Override    
public String getSOAPResponseXML(Object clientstub) {
    String returnValue = PaymentGatewayXMLThreadLocal.get();
    return returnValue;
} // getSOAPResponseXML

It appears there may be another way after all. After reading jax-ws-handlers, I saw that the handler can introduce an Application scoped variable. I changed the handler to do this:

private void storeSOAPMessageXml(SOAPMessageContext messageContext) {
String xml = getSOAPMessageXml(messageContext);
// YourPayXMLThreadLocal.set(xml);
// put it into the messageContext as well, but change scope
// default of handler Scope, and client can't read it from responsecontext!
messageContext.put(SOAP_RESPONSE_XML, xml);
messageContext.setScope(SOAP_RESPONSE_XML, MessageContext.Scope.APPLICATION );
} // storeSOAPMessageXml

The client just reads it like this:

@Override    
public String getSOAPResponseXML(Object clientstub) {
    String returnValue = null;
    // works (assuming a threadlocal is ok)
    // returnValue = YourPayXMLThreadLocal.get();

    BindingProvider bindingProvider = (BindingProvider) clientstub;
    // Thought this would work, but it doesn't - it returns null.        
    // Map<String, Object> requestContext = bindingProvider.getRequestContext();
    // String returnValue = (String) requestContext.get(JaxWsClientResponseXmlHandler.SOAP_RESPONSE_XML);

    // this works!!
    Map<String, Object> responseContext = bindingProvider.getResponseContext();
    System.out.println("has key? " + responseContext.containsKey(JaxWsClientResponseXmlHandler.SOAP_RESPONSE_XML));         
    returnValue = (String) responseContext.get(JaxWsClientResponseXmlHandler.SOAP_RESPONSE_XML);
    return returnValue;
} // getSOAPResponseXML

Answer

davidfmatheson picture davidfmatheson · May 20, 2013

If you just want to see the request, you can use the system property

-Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true

If you actually want to do something with the request, then a handler seems the natural solution. Perhaps use the request context to pass values to the handler? On the client:

((BindingProvider) port).getRequestContext().put("KEY", "VALUE");

In the handler:

String value = (String) messageContext.get("KEY");