2

I am currently developing a SOAP client with Spring WS. I followed the guides/tutorials and all is working fine. Except for one thing, error handling.

I have no control over the SOAP endpoint i am sending messages to. So i want to do some good exception handling in case of things going wrong. I encountered some HTTP errors in the testing scenarios i didn't count on. And i dont know how to handle them.

For example:

I send a SOAP message to the wrong URL. Which returned a HTTP 405 Method not allowed. (I send a POST to a GET only URL, it was a misconfiguration but still an error). In the body was literally a HTML error page from the SOAP endpoint. Ofcourse Spring couldn't handle this message and was throwing an exception. In the stacktrace it was clear it was a 405. But i want to extract the error code returned and log it in some other way (Or send it back to the user doing a request for sending the SOAP message).

These are some log messages i recieved after sending the SOAP message (other logging is omitted):

org.apache.http.wire                     :  << "HTTP/1.1 405 Method Not Allowed[\r][\n]"
o.a.h.impl.conn.DefaultClientConnection  : Receiving response: HTTP/1.1 405 Method Not Allowed
org.apache.http.headers                  : << HTTP/1.1 405 Method Not Allowed

Is there any way to catch and extract these HTTP error codes? If there is any more information needed, just ask. I am willing to provide as much info as i can but i dont know what else to post at this moment.

EDIT: Or is there any way to mock/test this with an integration test? I can't keep sending messages to the real SOAP endpoint. I don't get the same output from an integration test as from a real test (booting the app and sending messages to the real SOAP endpoint). I have set my logging levels for integration testing the same as for the real testing environment.

Thanks!

2 Answers 2

3

You can create your own wsTemplate by extending the spring WebServiceTemplate and overriding the handleError method and adding your custom implementation.

Not sure what version are you using but a common default implementation looks like this:

protected Object handleError(WebServiceConnection connection, WebServiceMessage request) throws IOException {
    if (logger.isDebugEnabled()) {
        logger.debug("Received error for request [" + request + "]");
    }
    throw new WebServiceTransportException(connection.getErrorMessage());
}

A more robust and desired approach would be to write your own org.springframework.ws.client.support.interceptor.ClientInterceptor and set in the wsTemplate through the setInterceptors method. Not sure if what you can get out of the MessageContext object will be enough to meet your requirements but all soap faults will be intercepted this way.

public class MyClientInterceptor implements ClientInterceptor {

    public boolean handleFault(MessageContext messageContext){
       // custom implementation
    }

    public boolean handleRequest(MessageContext messageContext){
       return true;
    }

    public boolean handleResponse(MessageContext messageContext){
       return true;
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

I tried this implementation, it seems to work but not optimally. I tried implementing log.error("Error message: " + webServiceConnection.getErrorMessage()); in this method. It logs the following error in my logs: Error message: [405]. I would have to strip the response code out of the string returned by getErrorMessage(). I actually want to capture the HTTP header with the HTTP response code as logged by org.apache.http.headers as it seems more robust. Any way to do this?
I've edited my answer, not sure if you need the specific status code or just by detecting the soap fault would be enough to meet your requirements..
The problem is, the soap endpoint is not returning SOAP faults. But actual HTTP error codes with a HTML error page. Thats why i need to capture the HTTP return codes, to check if it is a 4xx or 5xx. Because i am not going to parse the HTML file. I was already experimenting with the ClientInterceptor. Maybe i will get to a solution today. Thanks tho!
2

Building on @artemisian's answer - you can cast the WebServiceConnection to its underlying implementation (I was using HttpComponentsConnection) and then use that to get access to the response headers / body.

protected Object handleError(WebServiceConnection connection, WebServiceMessage request) throws IOException {

    if(connection instanceof HttpComponentsConnection) {
        HttpComponentsConnection conn = (HttpComponentsConnection) connection;
        for(Iterator<String> it = conn.getResponseHeaderNames(); it.hasNext(); )
            for(Iterator<String> vt = conn.getResponseHeaders(it.next()); vt.hasNext(); )
                ... do something ...
        conn.getHttpResponse().getEntity().writeTo( ... some stream ... );
    }

    if (logger.isDebugEnabled()) {
        logger.debug("Received error for request [" + request + "]");
    }
    throw new WebServiceTransportException(connection.getErrorMessage());

}

1 Comment

To get the error code, you can use: ((HttpComponentsConnection) connection).getHttpResponse().getStatusLine().getStatusCode()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.