Search code examples
springwebservice-client

Soap Fault Message Resolver isn't invoked after adding Wss4jSecurityInterceptor config


I have written a web service client (using Java Spring and JAXB Marshaller) that works with a 3rd party web service. When I send a valid request everything works well. When I send an invalid request then the web service server responds with a SOAP Fault. The client application just fails with a UnmarshallingFailureException

org.springframework.oxm.UnmarshallingFailureException: JAXB unmarshalling 
exception; nested exception is javax.xml.bind.UnmarshalException: 
unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault").

Appears to me that my ws client isn't able to decipher the SOAP fault returned by the web service. I wrote a custom FaultMessageResolver, but it doesn't get invoked (I set a breakpoint there but it doesn't hit. The FaultMessageResolver just worked fine before I added the Wss4jSecurityInterceptor for signature, encryption/decryption stuff). Here's the code:

public class VehicleServiceClientExceptionResolver implements FaultMessageResolver {

@Override
public void resolveFault(WebServiceMessage message) throws IOException {

    SoapMessage soapMessage = (SoapMessage) message;

    try {

        JAXBContext context = JAXBContext.newInstance(ErrorMessages.class);
        Unmarshaller unMarshaller = context.createUnmarshaller();
        ErrorMessages errorMessages = (ErrorMessages)unMarshaller.unmarshal(soapMessage.getSoapBody().getFault().getFaultDetail().getDetailEntries().next().getSource());

        if (errorMessages.getErrorMessage().size() > 0) {
            throw new VehicleServiceClientException(errorMessages);
        }

    } catch (JAXBException e) {
        LOGGER.debug(e.getMessage());
    }
}

}

And this custom soap fault resolver is injected into client side web service template like below:

<bean id="vehicleQuotationWebServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
    <constructor-arg ref="messageFactory"/>
    <property name="interceptors">
        <list>
            <ref bean="wsSecurityInterceptor"/>
        </list> 
    </property>
    <property name="marshaller" ref="vehicleQuotationMarshaller" />
    <property name="unmarshaller" ref="vehicleQuotationMarshaller" />
    <property name="messageSender" ref="urlMessageSender"/>
    <property name="faultMessageResolver" ref="vehicleServiceClientFaultMessageResolver" />
    <property name="defaultUri" value="https://*********/*********Service"/>
</bean>

The most weird thing is although I got that unmarshall exception, I did see the encrypted server response was decrypted in my eclipse console when I change the log level from INFO to DEBUG, I am not sure where this DigesterOutputStream comes from, but I think it might be the key to solve this.

Anyone got any idea? Thanks!

DEBUG p.xml.dsig.internal.DigesterOutputStream:
<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Id-af090516-9e00-4590-b481-c78e59d6b2fc"><soapenv:Fault><faultcode>soapenv:Client.Validation</faultcode><faultstring</faultstring><detail><em:ErrorMessages xmlns:em="urn:ford/errormessage/v1.0"><em:ErrorMessage><em:ErrorCode>GLSE903100</em:ErrorCode><em:ErrorDescription> CTT System Quote Id already exists ('1041')</em:ErrorDescription><em:ErrorTime>2014-05-16T15:13:20</em:ErrorTime></em:ErrorMessage></em:ErrorMessages></detail></soapenv:Fault></soapenv:Body>

Solution

  • I found the solution here: Adding a WebServiceMessageExtractor<Object> to:

    WebServiceTemplate.sendAndReceive(
        new WebServiceMessageCallback(),
        new WebServiceMessageExtractor<Object>())
    

    does the trick.