So, my project uses spring-ws for exposing web services, as well as calling web services exposed by the other application. I use xjc for generating client classes directly from the wsdl of the target application. It looked like this
WebServiceTemplate wsTemplate = getWsTemplate(); Request xmlRequest = objectFactory.createRequest(); JAXBElement<Request> requestPayload = objectFactory.createRequest(xmlRequest); JAXBElement<Response> xmlResponse = (JAXBElement<Response>)wsTemplate.marshalSendAndReceive(requestPayload);When the server responded with SOAP Fault I was getting an unmarshalling exception... It was strange for me and I didn't know why. I started to debug, and noticed that spring uses a HTTPConnection under the hood for verifying the HTTP response status. If it is 500, it correctly detects that the response is a SOAP fault. That was not the case in my scenario, as the web service I was calling was returning a HTTP 200... Fortunatelly, spring-ws has something for that the checkConnectionForFault parameter. So I thought I was saved. But no - maybe it would be enough for a standard case, but I had my message encrypted by WS-Security rules, so it structure could not be validated for soap fault standard untill it was deciphered. So I needed to delay this check to the last possible moment. This is what I came up with
WebServiceTemplate wsTemplate = getWsTemplate();
Request xmlRequest = objectFactory.createRequest();
JAXBElement<Request > requestPayload = objectFactory.createRequest(xmlRequest);
JAXBElement<Response> xmlResponse = (JAXBElement<Response>)wsTemplate.sendAndReceive(wsTemplate.getDefaultUri(), new WebServiceMessageCallback() {
@Override
public void doWithMessage(final WebServiceMessage request) throws IOException, TransformerException {
if (requestPayload != null) {
if (marshaller == null) {
throw new IllegalStateException("No marshaller registered. Check configuration of WebServiceTemplate.");
}
MarshallingUtils.marshal(marshaller, requestPayload, request);
}
}
}, new WebServiceMessageExtractor<Object>() {
@Override
public Object extractData(final WebServiceMessage response) throws IOException {
if (unmarshaller == null) {
throw new IllegalStateException("No unmarshaller registered. Check configuration of WebServiceTemplate.");
}
if (hasFault(response)) {
throw new SoapFaultClientException((SoapMessage) response);
} else {
return MarshallingUtils.unmarshal(unmarshaller, response);
}
}
protected boolean hasFault(final WebServiceMessage response) throws IOException {
if (response instanceof FaultAwareWebServiceMessage) {
FaultAwareWebServiceMessage faultMessage = (FaultAwareWebServiceMessage) response;
return faultMessage.hasFault();
}
return false;
}
});
This is mostly the same that is done by spring when you call just marshalSendAndReceive method. But this way, I could influence the extractData in the WebServiceMessageExtractor to add additional hasFault check. At this point, my message is already decrypted, so I can validate if it contains soap fault.
it helped. Thank you for share.
OdpowiedzUsuń