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 super V> callback) {
- callback.onSuccess(this.value);
+ addCallback(callback, callback);
}
+ @Override
+ public void addCallback(SuccessCallback super V> 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 super T> 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 super T> 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 super T> callback) {
+ addCallback(callback, callback);
+ }
+
+ @Override
+ public void addCallback(final SuccessCallback super T> 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 super T> 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 super T> 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 super T> 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 super T> callback) {
this.callbacks.addCallback(callback);
}
+ @Override
+ public void addCallback(SuccessCallback super T> 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 super T> callback) {
this.listenableFuture.addCallback(callback);
}
+ @Override
+ public void addCallback(SuccessCallback super T> 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 super T> callback) {
this.registry.addCallback(callback);
}
+ @Override
+ public void addCallback(SuccessCallback super T> 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 super ClientHttpResponse> cal
this.callbacks.addCallback(callback);
}
+ public void addSuccessCallback(SuccessCallback super ClientHttpResponse> 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 super ClientHttpResponse> callback) {
this.callback.addCallback(callback);
}
+
+ @Override
+ public void addCallback(SuccessCallback super ClientHttpResponse> 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 super URI> callback) {
+ addCallback(callback, callback);
+ }
+
+ @Override
+ public void addCallback(final SuccessCallback super URI> 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 super Set> callback) {
+ public void addCallback(final ListenableFutureCallback super Set> callback) {
+ addCallback(callback, callback);
+ }
+
+ @Override
+ public void addCallback(final SuccessCallback super Set> 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