From b119a9c82cfbbd7d652d5adc6e3564383adb1479 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 10 Apr 2015 12:52:15 +0200 Subject: [PATCH] FutureAdapter should wrap RuntimeExceptions RuntimeExceptions thrown from FutureAdapter.adapt() should be wrapped in an ExecutionException, not thrown as is. Issue: SPR-12887 --- .../util/concurrent/FutureAdapter.java | 6 +++ .../AsyncRestTemplateIntegrationTests.java | 53 +++++++++++++++---- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java b/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java index b0c6e182831..ad046957a6b 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java @@ -107,6 +107,12 @@ public abstract class FutureAdapter implements Future { this.state = State.FAILURE; throw ex; } + catch (RuntimeException ex) { + ExecutionException execEx = new ExecutionException(ex); + this.result = execEx; + this.state = State.FAILURE; + throw execEx; + } default: throw new IllegalStateException(); } diff --git a/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java index b6127832862..22ad9cd5ee0 100644 --- a/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java @@ -20,6 +20,7 @@ import java.net.URI; import java.nio.charset.Charset; import java.util.EnumSet; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.junit.Before; @@ -317,16 +318,47 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa } @Test - public void notFound() throws Exception { + public void identicalExceptionThroughGetAndCallback() throws Exception { + final HttpClientErrorException[] callbackException = new HttpClientErrorException[1]; + + ListenableFuture future = template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, null); + future.addCallback(new ListenableFutureCallback() { + @Override + public void onSuccess(Object result) { + fail("onSuccess not expected"); + } + @Override + public void onFailure(Throwable t) { + assertTrue(t instanceof HttpClientErrorException); + callbackException[0] = (HttpClientErrorException) t; + } + }); + + try { + future.get(); + fail("Exception expected"); + } + catch (ExecutionException ex) { + Throwable cause = ex.getCause(); + assertTrue(cause instanceof HttpClientErrorException); + assertSame(callbackException[0], cause); + } + } + + @Test + public void notFoundGet() throws Exception { try { Future future = template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, null); future.get(); fail("HttpClientErrorException expected"); } - catch (HttpClientErrorException ex) { - assertEquals(HttpStatus.NOT_FOUND, ex.getStatusCode()); - assertNotNull(ex.getStatusText()); - assertNotNull(ex.getResponseBodyAsString()); + catch (ExecutionException ex) { + assertTrue(ex.getCause() instanceof HttpClientErrorException); + HttpClientErrorException cause = (HttpClientErrorException)ex.getCause(); + + assertEquals(HttpStatus.NOT_FOUND, cause.getStatusCode()); + assertNotNull(cause.getStatusText()); + assertNotNull(cause.getResponseBodyAsString()); } } @@ -372,10 +404,13 @@ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCa future.get(); fail("HttpServerErrorException expected"); } - catch (HttpServerErrorException ex) { - assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, ex.getStatusCode()); - assertNotNull(ex.getStatusText()); - assertNotNull(ex.getResponseBodyAsString()); + catch (ExecutionException ex) { + assertTrue(ex.getCause() instanceof HttpServerErrorException); + HttpServerErrorException cause = (HttpServerErrorException)ex.getCause(); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, cause.getStatusCode()); + assertNotNull(cause.getStatusText()); + assertNotNull(cause.getResponseBodyAsString()); } }