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ń