Reputation: 2362
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>
Upvotes: 10
Views: 39326
Reputation: 569
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.
Upvotes: 3
Reputation: 63
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.
Upvotes: 3
Reputation: 129
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.
Upvotes: 10
Reputation: 2362
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?
Upvotes: 2
Reputation: 391
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.
Upvotes: 5