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

2

3

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
Arthrospore answered 20/5, 2013 at 15:1 Comment(0)
B
1

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");
Bissau answered 20/5, 2013 at 15:15 Comment(2)
I don't just want to see it, I want to log it to the database based on a transaction id# known by the client. I create this log entry PRIOR to sending the request. The handler wouldn't know about this transaction id# (it's not part of the request - it's my log). I want to know the resulting SOAP Response from the call. In apache Axis, I responded to another therad here. I figured a newer WS client stack would make this easier. So threadlocal is ok?Arthrospore
The jax-ws client unmarshalls the soap response into a strongly typed object from the payment gateway. Unfortunately, we've run into situations in the past where we get undocumented error codes and strings longer than expected/documented. Then our routines to simply persist the response would fail. That's when the fail-safe response column (clob) was added to the log as well. If I went your route, my handler would have to be able to persist this response, as when I tried putting the KEY/VALUE pair in the handler and reading in the client it was null. I use MyBatis/Spring as well if that helps.Arthrospore
Z
0

Unfortunately, the only way to get the XML before sending it and without using Message Handlers is to marshall it your self (see JAXB). This will give you an XML representation of the data, however it might not LOOK exactly the same as the message sent to the WS. Differences might arise in the way that namespaces are used, ect., but most importantly you will not get the whole SOAP envelope, just the XML data for the header you choose to marshall.

Zoniazoning answered 21/5, 2013 at 7:1 Comment(1)
I saw a response here regarding that. I definitely don't want the xml before sending it. I want the response. SOAPFaults and other errors prevent the WS Client from unmarshalling to the Strongly typed Java object. So, I'd like to have the XML response payload. That gets back to my original question: Is there any any reason I couldn't put it in a ThreadLocal in the handler to retrieve from the client? Thanks, again for all the help.Arthrospore

© 2022 - 2024 — McMap. All rights reserved.