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 4e6f7498a1..7b37673302 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
@@ -18,44 +18,48 @@ package org.springframework.util.concurrent;
import org.springframework.util.Assert;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.atomic.AtomicReference;
/**
- * A {@link ListenableFuture} whose value can be set by the {@link #set(Object)} or
+ * A {@link org.springframework.util.concurrent.ListenableFuture ListenableFuture}
+ * whose value can be set via {@link #set(Object)} or
* {@link #setException(Throwable)}. It may also be cancelled.
*
*
Inspired by {@code com.google.common.util.concurrent.SettableFuture}.
*
* @author Mattias Severson
+ * @author Rossen Stoyanchev
* @since 4.1
*/
public class SettableListenableFuture implements ListenableFuture {
- private final SettableFuture settableFuture = new SettableFuture();
- private final ListenableFutureCallbackRegistry registry = new ListenableFutureCallbackRegistry();
+ private final SettableTask settableTask;
+ private final ListenableFutureTask listenableFuture;
+
+
+ public SettableListenableFuture() {
+ this.settableTask = new SettableTask();
+ this.listenableFuture = new ListenableFutureTask(this.settableTask);
+ }
/**
* Set the value of this future. This method will return {@code true} if
* the value was set successfully, or {@code false} if the future has already
* been set or cancelled.
- *
* @param value the value that will be set.
* @return {@code true} if the value was successfully set, else {@code false}.
*/
public boolean set(T value) {
- boolean setValue = this.settableFuture.setValue(value);
- if (setValue) {
- this.registry.success(value);
+ boolean success = this.settableTask.setValue(value);
+ if (success) {
+ this.listenableFuture.run();
}
- return setValue;
+ return success;
}
/**
@@ -66,66 +70,66 @@ public class SettableListenableFuture implements ListenableFuture {
* @return {@code true} if the exception was successfully set, else {@code false}.
*/
public boolean setException(Throwable exception) {
- Assert.notNull(exception, "exception must not be null");
- boolean setException = this.settableFuture.setThrowable(exception);
- if (setException) {
- this.registry.failure(exception);
+ Assert.notNull(exception, "'exception' must not be null");
+ boolean success = this.settableTask.setValue(exception);
+ if (success) {
+ this.listenableFuture.run();
}
- return setException;
+ return success;
}
- @Override
- public void addCallback(ListenableFutureCallback super T> callback) {
- this.registry.addCallback(callback);
- }
+ @Override
+ public void addCallback(ListenableFutureCallback super T> callback) {
+ this.listenableFuture.addCallback(callback);
+ }
- @Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- boolean cancelled = this.settableFuture.cancel(mayInterruptIfRunning);
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ this.settableTask.setCancelled();
+ boolean cancelled = this.listenableFuture.cancel(mayInterruptIfRunning);
if (cancelled && mayInterruptIfRunning) {
interruptTask();
}
return cancelled;
- }
+ }
- @Override
- public boolean isCancelled() {
- return this.settableFuture.isCancelled();
- }
+ @Override
+ public boolean isCancelled() {
+ return this.listenableFuture.isCancelled();
+ }
- @Override
- public boolean isDone() {
- return this.settableFuture.isDone();
- }
+ @Override
+ public boolean isDone() {
+ return this.listenableFuture.isDone();
+ }
/**
* Retrieve the value.
- *
Will return the value if it has been set by calling {@link #set(Object)}, throw
- * an {@link ExecutionException} if the {@link #setException(Throwable)} has been
- * called, throw a {@link CancellationException} if the future has been cancelled, or
- * throw an {@link IllegalStateException} if neither a value, nor an exception has
- * been set.
+ *
Will return the value if it has been set via {@link #set(Object)},
+ * throw an {@link java.util.concurrent.ExecutionException} if it has been
+ * set via {@link #setException(Throwable)} or throw a
+ * {@link java.util.concurrent.CancellationException} if it has been cancelled.
* @return The value associated with this future.
*/
- @Override
- public T get() throws InterruptedException, ExecutionException {
- return this.settableFuture.get();
- }
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ return this.listenableFuture.get();
+ }
/**
* Retrieve the value.
- *
Will return the value if it has been by calling {@link #set(Object)}, throw an
- * {@link ExecutionException} if the {@link #setException(Throwable)}
- * has been called, throw a {@link java.util.concurrent.CancellationException} if the
- * future has been cancelled.
+ *
Will return the value if it has been set via {@link #set(Object)},
+ * throw an {@link java.util.concurrent.ExecutionException} if it has been
+ * set via {@link #setException(Throwable)} or throw a
+ * {@link java.util.concurrent.CancellationException} if it has been cancelled.
* @param timeout the maximum time to wait.
* @param unit the time unit of the timeout argument.
* @return The value associated with this future.
*/
@Override
- public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
- return this.settableFuture.get(timeout, unit);
- }
+ public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ return this.listenableFuture.get(timeout, unit);
+ }
/**
* Subclasses can override this method to implement interruption of the future's
@@ -138,143 +142,33 @@ public class SettableListenableFuture implements ListenableFuture {
}
- /**
- * Helper class that keeps track of the state of this future.
- * @param The type of value to be set.
- */
- private static class SettableFuture implements Future {
+ private static class SettableTask implements Callable {
- private final ReadWriteLock lock = new ReentrantReadWriteLock(true);
- private final CountDownLatch latch = new CountDownLatch(1);
- private T value;
- private Throwable throwable;
- private State state = State.INITIALIZED;
+ private static final String NO_VALUE = SettableListenableFuture.class.getName() + ".NO_VALUE";
+ private final AtomicReference