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 erro
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.
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.
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.