diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncResult.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncResult.java index 896e25ddc8bf..9b3a86a49a3f 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncResult.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncResult.java @@ -18,8 +18,10 @@ import java.util.concurrent.TimeUnit; +import org.springframework.util.concurrent.FailureCallback; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; +import org.springframework.util.concurrent.SuccessCallback; /** * A pass-through {@code Future} handle that can be used for method signatures @@ -74,7 +76,15 @@ public V get(long timeout, TimeUnit unit) { @Override public void addCallback(ListenableFutureCallback callback) { - callback.onSuccess(this.value); + addCallback(callback, callback); } + @Override + public void addCallback(SuccessCallback successCallback, FailureCallback failureCallback) { + try { + successCallback.onSuccess(this.value); + } catch(Throwable t) { + failureCallback.onFailure(t); + } + } } diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/FailureCallback.java b/spring-core/src/main/java/org/springframework/util/concurrent/FailureCallback.java new file mode 100644 index 000000000000..ebfdfa6470bd --- /dev/null +++ b/spring-core/src/main/java/org/springframework/util/concurrent/FailureCallback.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.util.concurrent; + +/** + * Defines the contract for failure callbacks that accept the result of a + * {@link ListenableFuture}. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public interface FailureCallback { + + /** + * Called when the {@link ListenableFuture} fails to complete. + * @param t the exception that triggered the failure + */ + void onFailure(Throwable t); + +} diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFuture.java b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFuture.java index f95b48c0a49c..6250b08f583f 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFuture.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFuture.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ *

Inspired by {@code com.google.common.util.concurrent.ListenableFuture}. * @author Arjen Poutsma + * @author Sebastien Deleuze * @since 4.0 */ public interface ListenableFuture extends Future { @@ -37,4 +38,15 @@ public interface ListenableFuture extends Future { */ void addCallback(ListenableFutureCallback callback); + /** + * Registers the given success and failure callbacks to this {@code ListenableFuture}. + * The callback will be triggered when this {@code Future} is complete or, if it is + * already complete immediately. This is a Java 8 lambdas compliant alternative to + * {@link #addCallback(ListenableFutureCallback)}. + * @param successCallback the success callback to register + * @param failureCallback the failure callback to register + * @since 4.1 + */ + void addCallback(SuccessCallback successCallback, FailureCallback failureCallback); + } diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureAdapter.java b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureAdapter.java index 4123e0b53855..9613e6ee2a53 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureAdapter.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,12 +44,18 @@ protected ListenableFutureAdapter(ListenableFuture adaptee) { @Override public void addCallback(final ListenableFutureCallback callback) { + addCallback(callback, callback); + } + + @Override + public void addCallback(final SuccessCallback successCallback, + final FailureCallback failureCallback) { ListenableFuture listenableAdaptee = (ListenableFuture) getAdaptee(); listenableAdaptee.addCallback(new ListenableFutureCallback() { @Override public void onSuccess(S result) { try { - callback.onSuccess(adaptInternal(result)); + successCallback.onSuccess(adaptInternal(result)); } catch (ExecutionException ex) { Throwable cause = ex.getCause(); @@ -62,8 +68,9 @@ public void onSuccess(S result) { @Override public void onFailure(Throwable t) { - callback.onFailure(t); + failureCallback.onFailure(t); } }); } + } diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallback.java b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallback.java index d112f9fa1423..633794458cb5 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallback.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,20 +21,9 @@ * {@link ListenableFuture}. * * @author Arjen Poutsma + * @author Sebastien Deleuze * @since 4.0 */ -public interface ListenableFutureCallback { - - /** - * Called when the {@link ListenableFuture} successfully completes. - * @param result the result - */ - void onSuccess(T result); - - /** - * Called when the {@link ListenableFuture} fails to complete. - * @param t the exception that triggered the failure - */ - void onFailure(Throwable t); +public interface ListenableFutureCallback extends SuccessCallback, FailureCallback { } diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallbackRegistry.java b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallbackRegistry.java index ca76f6d72fa7..5794cf85a84e 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallbackRegistry.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallbackRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,12 +27,16 @@ *

Inspired by {@code com.google.common.util.concurrent.ExecutionList}. * * @author Arjen Poutsma + * @author Sebastien Deleuze * @since 4.0 */ public class ListenableFutureCallbackRegistry { - private final Queue> callbacks = - new LinkedList>(); + private final Queue> successCallbacks = + new LinkedList>(); + + private final Queue failureCallbacks = + new LinkedList(); private State state = State.NEW; @@ -52,7 +56,8 @@ public void addCallback(ListenableFutureCallback callback) { synchronized (mutex) { switch (state) { case NEW: - callbacks.add(callback); + successCallbacks.add(callback); + failureCallbacks.add(callback); break; case SUCCESS: callback.onSuccess((T)result); @@ -64,6 +69,50 @@ public void addCallback(ListenableFutureCallback callback) { } } + /** + * Adds the given success callback to this registry. + * @param callback the success callback to add + * + * @since 4.1 + */ + @SuppressWarnings("unchecked") + public void addSuccessCallback(SuccessCallback callback) { + Assert.notNull(callback, "'callback' must not be null"); + + synchronized (mutex) { + switch (state) { + case NEW: + successCallbacks.add(callback); + break; + case SUCCESS: + callback.onSuccess((T)result); + break; + } + } + } + + /** + * Adds the given failure callback to this registry. + * @param callback the failure callback to add + * + * @since 4.1 + */ + @SuppressWarnings("unchecked") + public void addFailureCallback(FailureCallback callback) { + Assert.notNull(callback, "'callback' must not be null"); + + synchronized (mutex) { + switch (state) { + case NEW: + failureCallbacks.add(callback); + break; + case FAILURE: + callback.onFailure((Throwable) result); + break; + } + } + } + /** * Triggers a {@link ListenableFutureCallback#onSuccess(Object)} call on all added * callbacks with the given result @@ -74,8 +123,8 @@ public void success(T result) { state = State.SUCCESS; this.result = result; - while (!callbacks.isEmpty()) { - callbacks.poll().onSuccess(result); + while (!successCallbacks.isEmpty()) { + successCallbacks.poll().onSuccess(result); } } } @@ -90,8 +139,8 @@ public void failure(Throwable t) { state = State.FAILURE; this.result = t; - while (!callbacks.isEmpty()) { - callbacks.poll().onFailure(t); + while (!failureCallbacks.isEmpty()) { + failureCallbacks.poll().onFailure(t); } } } diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureTask.java b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureTask.java index b61f885f3c55..393a6e64c737 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureTask.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,6 +57,12 @@ public void addCallback(ListenableFutureCallback callback) { this.callbacks.addCallback(callback); } + @Override + public void addCallback(SuccessCallback successCallback, FailureCallback failureCallback) { + this.callbacks.addSuccessCallback(successCallback); + this.callbacks.addFailureCallback(failureCallback); + } + @Override protected final void done() { Throwable cause; diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/SettableListenableFuture.java b/spring-core/src/main/java/org/springframework/util/concurrent/SettableListenableFuture.java index 3f64506528fc..4ab87ed6b579 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/SettableListenableFuture.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/SettableListenableFuture.java @@ -84,6 +84,11 @@ public void addCallback(ListenableFutureCallback callback) { this.listenableFuture.addCallback(callback); } + @Override + public void addCallback(SuccessCallback successCallback, FailureCallback failureCallback) { + this.listenableFuture.addCallback(successCallback, failureCallback); + } + @Override public boolean cancel(boolean mayInterruptIfRunning) { this.settableTask.setCancelled(); diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/SuccessCallback.java b/spring-core/src/main/java/org/springframework/util/concurrent/SuccessCallback.java new file mode 100644 index 000000000000..65f3041162fe --- /dev/null +++ b/spring-core/src/main/java/org/springframework/util/concurrent/SuccessCallback.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.util.concurrent; + +/** + * Defines the contract for success callbacks that accept the result of a + * {@link ListenableFuture}. + * + * @author Sebastien Deleuze + * @since 4.1 + */ +public interface SuccessCallback { + + /** + * Called when the {@link ListenableFuture} successfully completes. + * @param result the result + */ + void onSuccess(T result); + +} diff --git a/spring-core/src/test/java/org/springframework/util/concurrent/ListenableFutureTaskTests.java b/spring-core/src/test/java/org/springframework/util/concurrent/ListenableFutureTaskTests.java index 84d351e18172..0644f631ef59 100644 --- a/spring-core/src/test/java/org/springframework/util/concurrent/ListenableFutureTaskTests.java +++ b/spring-core/src/test/java/org/springframework/util/concurrent/ListenableFutureTaskTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,8 +24,13 @@ import static org.junit.Assert.fail; import org.junit.Test; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + /** * @author Arjen Poutsma + * @author Sebastien Deleuze */ public class ListenableFutureTaskTests { @@ -77,6 +82,33 @@ public void onFailure(Throwable t) { task.run(); } + @Test + public void successWithLambdas() throws ExecutionException, InterruptedException { + final String s = "Hello World"; + Callable callable = () -> s; + SuccessCallback successCallback = mock(SuccessCallback.class); + FailureCallback failureCallback = mock(FailureCallback.class); + ListenableFutureTask task = new ListenableFutureTask<>(callable); + task.addCallback(successCallback, failureCallback); + task.run(); + verify(successCallback).onSuccess(s); + verifyZeroInteractions(failureCallback); + } + @Test + public void failureWithLambdas() throws ExecutionException, InterruptedException { + final String s = "Hello World"; + IOException ex = new IOException(s); + Callable callable = () -> { + throw ex; + }; + SuccessCallback successCallback = mock(SuccessCallback.class); + FailureCallback failureCallback = mock(FailureCallback.class); + ListenableFutureTask task = new ListenableFutureTask<>(callable); + task.addCallback(successCallback, failureCallback); + task.run(); + verify(failureCallback).onFailure(ex); + verifyZeroInteractions(successCallback); + } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/AbstractPromiseToListenableFutureAdapter.java b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/AbstractPromiseToListenableFutureAdapter.java index 02d0d19ccafb..ca34a986443f 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/AbstractPromiseToListenableFutureAdapter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/AbstractPromiseToListenableFutureAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,11 @@ import java.util.concurrent.TimeoutException; import org.springframework.util.Assert; +import org.springframework.util.concurrent.FailureCallback; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.util.concurrent.ListenableFutureCallbackRegistry; +import org.springframework.util.concurrent.SuccessCallback; import reactor.core.composable.Promise; import reactor.function.Consumer; @@ -106,4 +108,9 @@ public void addCallback(ListenableFutureCallback callback) { this.registry.addCallback(callback); } + @Override + public void addCallback(SuccessCallback successCallback, FailureCallback failureCallback) { + this.registry.addSuccessCallback(successCallback); + this.registry.addFailureCallback(failureCallback); + } } diff --git a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsAsyncClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsAsyncClientHttpRequest.java index 0f7d277de226..8aa1feee85cd 100644 --- a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsAsyncClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsAsyncClientHttpRequest.java @@ -29,12 +29,15 @@ import org.apache.http.nio.entity.NByteArrayEntity; import org.apache.http.protocol.HttpContext; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; +import org.springframework.util.concurrent.FailureCallback; import org.springframework.util.concurrent.FutureAdapter; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.util.concurrent.ListenableFutureCallbackRegistry; +import org.springframework.util.concurrent.SuccessCallback; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; + /** * {@link ClientHttpRequest} implementation that uses Apache HttpComponents HttpClient to @@ -101,6 +104,14 @@ public void addCallback(ListenableFutureCallback cal this.callbacks.addCallback(callback); } + public void addSuccessCallback(SuccessCallback callback) { + this.callbacks.addSuccessCallback(callback); + } + + public void addFailureCallback(FailureCallback callback) { + this.callbacks.addFailureCallback(callback); + } + @Override public void completed(HttpResponse result) { this.callbacks.success(new HttpComponentsAsyncClientHttpResponse(result)); @@ -136,6 +147,12 @@ protected ClientHttpResponse adapt(HttpResponse response) { public void addCallback(ListenableFutureCallback callback) { this.callback.addCallback(callback); } + + @Override + public void addCallback(SuccessCallback successCallback, FailureCallback failureCallback) { + this.callback.addSuccessCallback(successCallback); + this.callback.addFailureCallback(failureCallback); + } } diff --git a/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java index 03ce53f4d2dd..5e7a0fd42191 100644 --- a/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,9 +44,11 @@ import org.springframework.http.client.support.AsyncHttpAccessor; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.util.Assert; +import org.springframework.util.concurrent.FailureCallback; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureAdapter; import org.springframework.util.concurrent.ListenableFutureCallback; +import org.springframework.util.concurrent.SuccessCallback; import org.springframework.web.util.UriTemplate; /** @@ -254,15 +256,21 @@ private static ListenableFuture extractLocationHeader(final ListenableFutur @Override public void addCallback(final ListenableFutureCallback callback) { + addCallback(callback, callback); + } + + @Override + public void addCallback(final SuccessCallback successCallback, + final FailureCallback failureCallback) { headersFuture.addCallback(new ListenableFutureCallback() { @Override public void onSuccess(HttpHeaders result) { - callback.onSuccess(result.getLocation()); + successCallback.onSuccess(result.getLocation()); } @Override public void onFailure(Throwable t) { - callback.onFailure(t); + failureCallback.onFailure(t); } }); } @@ -391,17 +399,21 @@ private static ListenableFuture> extractAllowHeader(final Listen return new ListenableFuture>() { @Override - public void addCallback( - final ListenableFutureCallback> callback) { + public void addCallback(final ListenableFutureCallback> callback) { + addCallback(callback, callback); + } + + @Override + public void addCallback(final SuccessCallback> successCallback, final FailureCallback failureCallback) { headersFuture.addCallback(new ListenableFutureCallback() { @Override public void onSuccess(HttpHeaders result) { - callback.onSuccess(result.getAllow()); + successCallback.onSuccess(result.getAllow()); } @Override public void onFailure(Throwable t) { - callback.onFailure(t); + failureCallback.onFailure(t); } }); } 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 5813633b9390..fc170689e9aa 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 @@ -45,6 +45,7 @@ /** * @author Arjen Poutsma + * @author Sebastien Deleuze */ public class AsyncRestTemplateIntegrationTests extends AbstractJettyServerTestCase { @@ -98,6 +99,21 @@ public void onFailure(Throwable t) { } } + @Test + public void getEntityCallbackWithLambdas() throws ExecutionException, InterruptedException { + ListenableFuture> + futureEntity = template.getForEntity(baseUrl + "/{method}", String.class, "get"); + futureEntity.addCallback((entity) -> { + assertEquals("Invalid content", helloWorld, entity.getBody()); + assertFalse("No headers", entity.getHeaders().isEmpty()); + assertEquals("Invalid content-type", textContentType, entity.getHeaders().getContentType()); + assertEquals("Invalid status code", HttpStatus.OK, entity.getStatusCode()); + }, (t) -> fail(t.getMessage())); + // wait till done + while (!futureEntity.isDone()) { + } + } + @Test public void getNoResponse() throws ExecutionException, InterruptedException { Future> @@ -164,6 +180,15 @@ public void onFailure(Throwable t) { } } + @Test + public void headForHeadersCallbackWithLambdas() throws ExecutionException, InterruptedException { + ListenableFuture headersFuture = template.headForHeaders(baseUrl + "/get"); + headersFuture.addCallback(result -> assertTrue("No Content-Type header", + result.containsKey("Content-Type")), t -> fail(t.getMessage())); + while (!headersFuture.isDone()) { + } + } + @Test public void postForLocation() throws URISyntaxException, ExecutionException, InterruptedException { @@ -202,6 +227,22 @@ public void onFailure(Throwable t) { } } + @Test + public void postForLocationCallbackWithLambdas() + throws URISyntaxException, ExecutionException, InterruptedException { + HttpHeaders entityHeaders = new HttpHeaders(); + entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15"))); + HttpEntity entity = new HttpEntity(helloWorld, entityHeaders); + final URI expected = new URI(baseUrl + "/post/1"); + ListenableFuture + locationFuture = template.postForLocation(baseUrl + "/{method}", entity, + "post"); + locationFuture.addCallback(result -> assertEquals("Invalid location", expected, result) + , t -> fail(t.getMessage())); + while (!locationFuture.isDone()) { + } + } + @Test public void postForEntity() throws URISyntaxException, ExecutionException, InterruptedException { @@ -235,6 +276,19 @@ public void onFailure(Throwable t) { } } + @Test + public void postForEntityCallbackWithLambdas() + throws URISyntaxException, ExecutionException, InterruptedException { + HttpEntity requestEntity = new HttpEntity<>(helloWorld); + ListenableFuture> + responseEntityFuture = template.postForEntity(baseUrl + "/{method}", requestEntity, + String.class, "post"); + responseEntityFuture.addCallback(result -> assertEquals("Invalid content", helloWorld, result.getBody()) + , t -> fail(t.getMessage())); + while (!responseEntityFuture.isDone()) { + } + } + @Test public void put() throws URISyntaxException, ExecutionException, InterruptedException { @@ -294,6 +348,15 @@ public void onFailure(Throwable t) { } } + @Test + public void deleteCallbackWithLambdas() + throws URISyntaxException, ExecutionException, InterruptedException { + ListenableFuture deletedFuture = template.delete(new URI(baseUrl + "/delete")); + deletedFuture.addCallback(result -> assertNull(result), t -> fail(t.getMessage())); + while (!deletedFuture.isDone()) { + } + } + @Test public void notFound() throws ExecutionException, InterruptedException { try { @@ -332,6 +395,22 @@ public void onFailure(Throwable t) { } } + @Test + public void notFoundCallbackWithLambdas() throws ExecutionException, InterruptedException { + ListenableFuture future = + template.execute(baseUrl + "/status/notfound", HttpMethod.GET, null, + null); + future.addCallback(result -> fail("onSuccess not expected"), t -> { + assertTrue(t instanceof HttpClientErrorException); + HttpClientErrorException ex = (HttpClientErrorException) t; + assertEquals(HttpStatus.NOT_FOUND, ex.getStatusCode()); + assertNotNull(ex.getStatusText()); + assertNotNull(ex.getResponseBodyAsString()); + }); + while (!future.isDone()) { + } + } + @Test public void serverError() throws ExecutionException, InterruptedException { try { @@ -368,6 +447,20 @@ public void onFailure(Throwable t) { } } + @Test + public void serverErrorCallbackWithLambdas() throws ExecutionException, InterruptedException { + ListenableFuture future = template.execute(baseUrl + "/status/server", HttpMethod.GET, null, null); + future.addCallback(result -> fail("onSuccess not expected"), t -> { + assertTrue(t instanceof HttpServerErrorException); + HttpServerErrorException ex = (HttpServerErrorException) t; + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, ex.getStatusCode()); + assertNotNull(ex.getStatusText()); + assertNotNull(ex.getResponseBodyAsString()); + }); + while (!future.isDone()) { + } + } + @Test public void optionsForAllow() throws URISyntaxException, ExecutionException, InterruptedException { @@ -386,8 +479,8 @@ public void optionsForAllowCallback() allowedFuture.addCallback(new ListenableFutureCallback>() { @Override public void onSuccess(Set result) { - assertEquals("Invalid response", - EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD, HttpMethod.TRACE), result); + assertEquals("Invalid response", EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, + HttpMethod.HEAD, HttpMethod.TRACE), result); } @Override @@ -399,6 +492,18 @@ public void onFailure(Throwable t) { } } + @Test + public void optionsForAllowCallbackWithLambdas() + throws URISyntaxException, ExecutionException, InterruptedException { + ListenableFuture> + allowedFuture = template.optionsForAllow(new URI(baseUrl + "/get")); + allowedFuture.addCallback(result -> assertEquals("Invalid response", + EnumSet.of(HttpMethod.GET, HttpMethod.OPTIONS, HttpMethod.HEAD,HttpMethod.TRACE), result), + t-> fail(t.getMessage())); + while (!allowedFuture.isDone()) { + } + } + @Test @SuppressWarnings({ "unchecked", "rawtypes" }) public void exchangeGet() throws Exception { @@ -436,6 +541,21 @@ public void onFailure(Throwable t) { } } + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void exchangeGetCallbackWithLambdas() throws Exception { + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.set("MyHeader", "MyValue"); + HttpEntity requestEntity = new HttpEntity(requestHeaders); + ListenableFuture> responseFuture = + template.exchange(baseUrl + "/{method}", HttpMethod.GET, requestEntity, + String.class, "get"); + responseFuture.addCallback(result -> assertEquals("Invalid content", helloWorld, + result.getBody()), t -> fail(t.getMessage())); + while (!responseFuture.isDone()) { + } + } + @Test public void exchangePost() throws Exception { HttpHeaders requestHeaders = new HttpHeaders(); @@ -476,7 +596,24 @@ public void onFailure(Throwable t) { }); while (!resultFuture.isDone()) { } + } + @Test + public void exchangePostCallbackWithLambdas() throws Exception { + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.set("MyHeader", "MyValue"); + requestHeaders.setContentType(MediaType.TEXT_PLAIN); + HttpEntity requestEntity = new HttpEntity(helloWorld, requestHeaders); + ListenableFuture> + resultFuture = template.exchange(baseUrl + "/{method}", HttpMethod.POST, + requestEntity, Void.class, "post"); + final URI expected =new URI(baseUrl + "/post/1"); + resultFuture.addCallback(result -> { + assertEquals("Invalid location", expected, result.getHeaders().getLocation()); + assertFalse(result.hasBody()); + }, t -> fail(t.getMessage())); + while (!resultFuture.isDone()) { + } } @Test