简体   繁体   中英

Getting raw XML SOAP-response on client side using ADB-stubs created by AXIS2

I access a SOAP service using ADB-stubs created by AXIS2. I would like to log the raw XML response of any Axis Fault, that is returned by the service. I can catch those errors as "ServiceError". However, I do not find a way to retreive the raw XML (see example below).

I found a way to access the raw XML request / response for regular processing, using getOMElement (see example below). However, this does not work for faults.

How do I get the raw XML fault using ADB stubs?

Example Java code:

    public void testRequest(String URL) throws AxisFault {
        MyServiceStub myservice = new MyServiceStub(URL);
        MyRequest req = new MyRequest();
        try {
            TypeMyFunctionResponse response = myservice.myFunction(req);

            // logging full soap response
            System.out.println("SOAP Response: "
                    + response.getOMElement(null,
                            OMAbstractFactory.getOMFactory())
                            .toStringWithConsume());
        } catch (RemoteException e) {
            //...
        } catch (ServiceError e) {
            // how to get the raw xml?
        }
    }

Example fault response, that I would like to fetch and log:

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
    <soapenv:Body>
        <soapenv:Fault>
            <soapenv:Code>
                <soapenv:Value>soapenv:Receiver</soapenv:Value>
            </soapenv:Code>
            <soapenv:Reason>
                <soapenv:Text xml:lang="en-US">service error</soapenv:Text>
            </soapenv:Reason>
            <soapenv:Detail>
                <ns1:error xmlns:ns1="http://www.somehost.com/webservices/someservice">
                    <ns1:code>500</ns1:code>
                    <ns1:messageText>some fault message</ns1:messageText>
                </ns1:error>
            </soapenv:Detail>
        </soapenv:Fault>
    </soapenv:Body>
</soapenv:Envelope>

Below is what you probably are looking for, yourStub is what you generated via wsdl2java and use below lines after you make your request. The message is set to lastOperation and sends it when you make the actual call:

request = yourStub._getServiceClient().getLastOperationContext().getMessageContext("Out")
              .getEnvelope().toString());

response = yourStub._getServiceClient().getLastOperationContext().getMessageContext("In")
              .getEnvelope().toString());

Hope that was helpful.

While this question is already answered well, I needed to do this earlier and had trouble finding a suitable answer that fit my constraints so I'm adding my own for posterity.

I needed to do this with Axis 2 version 1.4.1 for a recent project running JDK 1.4, which from what I'd read was not supported by the JAX-WS stubs. I ended up keeping the ADB stubs while capturing the input by wrapping SoapBuilder with my own builder class, copying the input stream and passing the copy to the SoapBuilder:

public class SOAPBuilderWrapper implements Builder {
    private String lastResponse;

    private SOAPBuilder builder = new SOAPBuilder();

    private static final int BUFFER_SIZE = 8192;

    public OMElement processDocument(InputStream inputStream,
            String contentType, MessageContext messageContext) throws AxisFault {
        ByteArrayOutputStream copiedStream = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead = inputStream.read(buffer);
            while (bytesRead > -1) {
                copiedStream.write(buffer, 0, bytesRead);
                bytesRead = inputStream.read(buffer);
            }
            lastResponse = copiedStream.toString();

        } catch (IOException e) {
            throw new AxisFault("Can't read from input stream", e);
        }
        return builder.processDocument(
                new ByteArrayInputStream(copiedStream.toByteArray()),
                contentType, messageContext);
    }

    public String getLastResponse() {
        return lastResponse;
    }
}

For various reasons, configuring using axis2.xml was problematic, so the wrapper was added programmatically with the following:

SoapBuilderWrapper responseCaptor = new SoapBuilderWrapper();
AxisConfiguration axisConfig = stub._getServiceClient().getAxisConfiguration();
axisConfig.addMessageBuilder("application/soap+xml", responseCaptor);
axisConfig.addMessageBuilder("text/xml", responseCaptor);

This allows responses to be retrieved using responseCaptor.getLastResponse() after the service is invoked.

Related to Ducane's reply:

response = yourStub._getServiceClient().getLastOperationContext().getMessageContext("In")
          .getEnvelope().toString());

fails with a com.ctc.wstx.exc.WstxIOException exception and the message: Attempted read on closed stream .

As suggested by joergl I changed my ADB-stubs to JAX-WS-ones using a "SOAPHandler" to log requests, responses and faults following the description here: http://www.mkyong.com/webservices/jax-ws/jax-ws-soap-handler-in-client-side/

My handler looks like this for logging the nicely formated XML using log4j:

public class RequestResponseHandler  implements SOAPHandler<SOAPMessageContext> {

    private static Logger log = Logger.getLogger(RequestResponseHandler.class);
    private Transformer transformer = null;
    private DocumentBuilderFactory docBuilderFactory = null;
    private DocumentBuilder docBuilder = null;

    public RequestResponseHandler() {
        try {
            transformer = TransformerFactory.newInstance().newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "5");
            docBuilderFactory = DocumentBuilderFactory.newInstance();
            docBuilder = docBuilderFactory.newDocumentBuilder();
        } catch (TransformerConfigurationException
                | TransformerFactoryConfigurationError
                | ParserConfigurationException e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void close(MessageContext arg0) {
    }

    @Override
    public boolean handleFault(SOAPMessageContext messageContext) {
        log(messageContext);
        return true;
    }

    @Override
    public boolean handleMessage(SOAPMessageContext messageContext) {
        log(messageContext);
        return true;
    }

    private void log(SOAPMessageContext messageContext) {
        String xml = "";
        SOAPMessage msg = messageContext.getMessage();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            msg.writeTo(out);
            xml = out.toString("UTF-8");
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }       

        String direction = "";
        Boolean outbound = (Boolean) messageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); 
        if (outbound) { 
            direction += "Request: \n"; 
        } else { 
            direction += "Response: \n";
        } 

        log.info(direction + getXMLprettyPrinted(xml));     
    }

    @Override
    public Set<QName> getHeaders() {
        return Collections.emptySet();
    }


    public String getXMLprettyPrinted(String xml) {

        if (transformer == null || docBuilder == null)
            return xml;

        InputSource ipXML = new InputSource(new StringReader(xml));
        Document doc;

        try {
            doc = docBuilder.parse(ipXML);
            StringWriter stringWriter = new StringWriter();
            StreamResult streamResult = new StreamResult(stringWriter);
            DOMSource domSource = new DOMSource(doc);
            transformer.transform(domSource, streamResult);
            return stringWriter.toString();
        } catch (SAXException | IOException | TransformerException e) {
            log.error(e.getMessage(), e);
            return xml;
        }
    }
}

In addition I wanted to reuse the raw XML in my application code. So I had to transfer this data from the SOAPHandler back to my client code. How to this was not too obvious. More about this problem can be found in this article: How to send additional fields to soap handler along with soapMessage?

For Axis2, those who do not have luxury of changing implementation/ or do not want to use JAS-WS for xyz reasons,

Found @Ducane's useful

 request = >yourStub._getServiceClient().getLastOperationContext().getMessageContext("Out") .getEnvelope().toString()); response = >yourStub._getServiceClient().getLastOperationContext().getMessageContext("In") .getEnvelope().toString()); 

As suggested in @dayer's answer

 response = >yourStub._getServiceClient().getLastOperationContext().getMessageContext("In") .getEnvelope().toString()); 

fails with a com.ctc.wstx.exc.WstxIOException exception and the message: >Attempted read on closed stream.

Not sure what is issue with "In" Message Lable,

But while searching, found following JIRA ticket https://issues.apache.org/jira/browse/AXIS2-5469 which points to https://issues.apache.org/jira/browse/AXIS2-5202 And in discussion found one of the WA to solve this issue using following code, I am able to listen Response message for the soapRequest.

stub._getServiceClient().getAxisService().addMessageContextListener(
new MessageContextListener() {
    public void attachServiceContextEvent(ServiceContext sc,
        MessageContext mc) {}
    public void attachEnvelopeEvent(MessageContext mc) {
        try
        { mc.getEnvelope().cloneOMElement().serialize(System.out); }
        catch (XMLStreamException e) {}
    }
});

As here MessageContextListner is Argument-Defined Anonymous Inner Classes it will have access to all enclosing variables, So i just defined a string class variable as latestSoapResponse and stored response for further use.

ByteArrayOutputStream baos = new ByteArrayOutputStream();
mc.getEnvelope().cloneOMElement().serialize(baos); 
latestSoapResponse=baos.toString();

Note that you need to add listener before generating soap request. and Request MessageContext will only be available once you generate soap request.

Also those who are just want raw soap request response for debugging purpose may see answer from @Sanker, here to enable Apache commons logging using JVM arguments.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM