Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AsyncRestTemplate changes in SPR-13413 made its harder to detect Client/Server errors [SPR-14353] #18925

Closed
spring-projects-issues opened this issue Jun 10, 2016 · 2 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jun 10, 2016

Oleg Mikhailenko opened SPR-14353 and commented

After migrating to 4.2.6 our response statuses were broken for our client side. It always showed 500 error status. It turned out our client's exception handler (@ControllerAdvice) caught ExecutionException even though a server responded with 4xx/5xx error statuses.
Spring's error hander for AsyncRestTemplate (DefaultResponseErrorHandler) contains code:

public void handleError(ClientHttpResponse response) throws IOException {
	HttpStatus statusCode = getHttpStatusCode(response);
	switch (statusCode.series()) {
		case CLIENT_ERROR:
			throw new HttpClientErrorException(statusCode, response.getStatusText(),
					response.getHeaders(), getResponseBody(response), getCharset(response));
		case SERVER_ERROR:
			throw new HttpServerErrorException(statusCode, response.getStatusText(),
					response.getHeaders(), getResponseBody(response), getCharset(response));
		default:
			throw new RestClientException("Unknown status code [" + statusCode + "]");
	}
}

It's OK but then (after #17992) any of these exceptions wrapped into ExecutionException in ResponseExtractorFuture (look throw new ExecutionException(ex);):

@Override
protected final T adapt(ClientHttpResponse response) throws ExecutionException {
	try {
		if (!getErrorHandler().hasError(response)) {
			logResponseStatus(this.method, this.url, response);
		}
		else {
			handleResponseError(this.method, this.url, response);
		}
		return convertResponse(response);
	}
	catch (Throwable ex) {
		throw new ExecutionException(ex);
	}
	finally {
		if (response != null) {
			response.close();
		}
	}
}

So as result it does not matter what error response sent by server HttpClientErrorException/HttpServerErrorException. It always wrapped to ExecutionException. And to fetch response status and body I have to get inner cause and cast to appropriate type. What is not so convenient.

It would be great if you could change the code of ResponseExtractorFuture to something like:

@Override
protected final T adapt(ClientHttpResponse response) throws ExecutionException {
        try {
            if (!getErrorHandler().hasError(response)) {
                logResponseStatus(this.method, this.url, response);
            }
            else {
                handleResponseError(this.method, this.url, response);
            }
            return convertResponse(response);
        }
        catch (RestClientException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new ExecutionException(ex);
        }
        finally {
            if (response != null) {
                response.close();
            }
        }
    }

Affects: 4.2.6

Issue Links:

1 votes, 5 watchers

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jun 22, 2016

Juergen Hoeller commented

Have you tried 4.3 GA? With #18863, we are already introspecting any exception cause there, including ExecutionException causes. It looks like your scenario should be covered there already?

In any case, point taken that 4.2.x does not handle such wrapped exceptions in @ExceptionHandler methods yet. I'll backport a minimal variant of #18863 for that purpose, in time for our upcoming 4.2.7 release.

As for the original motivation behind #17992, this still seems sound and in conformance with the java.util.concurrent.Future javadoc which says that ExecutionException should be thrown if "the computation threw an exception"... i.e. for any exception coming out of the target operation, allowing to differentiate between async infrastructure exceptions and actual computation exceptions.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

This turns out to be a bit awkward to backport isolation, so I'd rather recommend going to 4.3 GA / 4.3.1 straight away where you'll have full support for exception cause matching.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants