001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.util.concurrent;
018
019import java.util.concurrent.Callable;
020import java.util.concurrent.ExecutionException;
021import java.util.concurrent.TimeUnit;
022import java.util.concurrent.TimeoutException;
023
024import org.springframework.util.Assert;
025
026/**
027 * A {@link ListenableFuture} whose value can be set via {@link #set(Object)}
028 * or {@link #setException(Throwable)}. It may also get cancelled.
029 *
030 * <p>Inspired by {@code com.google.common.util.concurrent.SettableFuture}.
031 *
032 * @author Mattias Severson
033 * @author Rossen Stoyanchev
034 * @author Juergen Hoeller
035 * @since 4.1
036 */
037public class SettableListenableFuture<T> implements ListenableFuture<T> {
038
039        private static final Callable<Object> DUMMY_CALLABLE = new Callable<Object>() {
040                @Override
041                public Object call() throws Exception {
042                        throw new IllegalStateException("Should never be called");
043                }
044        };
045
046
047        private final SettableTask<T> settableTask = new SettableTask<T>();
048
049
050        /**
051         * Set the value of this future. This method will return {@code true} if the
052         * value was set successfully, or {@code false} if the future has already been
053         * set or cancelled.
054         * @param value the value that will be set
055         * @return {@code true} if the value was successfully set, else {@code false}
056         */
057        public boolean set(T value) {
058                return this.settableTask.setResultValue(value);
059        }
060
061        /**
062         * Set the exception of this future. This method will return {@code true} if the
063         * exception was set successfully, or {@code false} if the future has already been
064         * set or cancelled.
065         * @param exception the value that will be set
066         * @return {@code true} if the exception was successfully set, else {@code false}
067         */
068        public boolean setException(Throwable exception) {
069                Assert.notNull(exception, "Exception must not be null");
070                return this.settableTask.setExceptionResult(exception);
071        }
072
073        @Override
074        public void addCallback(ListenableFutureCallback<? super T> callback) {
075                this.settableTask.addCallback(callback);
076        }
077
078        @Override
079        public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) {
080                this.settableTask.addCallback(successCallback, failureCallback);
081        }
082
083        @Override
084        public boolean cancel(boolean mayInterruptIfRunning) {
085                boolean cancelled = this.settableTask.cancel(mayInterruptIfRunning);
086                if (cancelled && mayInterruptIfRunning) {
087                        interruptTask();
088                }
089                return cancelled;
090        }
091
092        @Override
093        public boolean isCancelled() {
094                return this.settableTask.isCancelled();
095        }
096
097        @Override
098        public boolean isDone() {
099                return this.settableTask.isDone();
100        }
101
102        /**
103         * Retrieve the value.
104         * <p>This method returns the value if it has been set via {@link #set(Object)},
105         * throws an {@link java.util.concurrent.ExecutionException} if an exception has
106         * been set via {@link #setException(Throwable)}, or throws a
107         * {@link java.util.concurrent.CancellationException} if the future has been cancelled.
108         * @return the value associated with this future
109         */
110        @Override
111        public T get() throws InterruptedException, ExecutionException {
112                return this.settableTask.get();
113        }
114
115        /**
116         * Retrieve the value.
117         * <p>This method returns the value if it has been set via {@link #set(Object)},
118         * throws an {@link java.util.concurrent.ExecutionException} if an exception has
119         * been set via {@link #setException(Throwable)}, or throws a
120         * {@link java.util.concurrent.CancellationException} if the future has been cancelled.
121         * @param timeout the maximum time to wait
122         * @param unit the unit of the timeout argument
123         * @return the value associated with this future
124         */
125        @Override
126        public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
127                return this.settableTask.get(timeout, unit);
128        }
129
130        /**
131         * Subclasses can override this method to implement interruption of the future's
132         * computation. The method is invoked automatically by a successful call to
133         * {@link #cancel(boolean) cancel(true)}.
134         * <p>The default implementation is empty.
135         */
136        protected void interruptTask() {
137        }
138
139
140        private static class SettableTask<T> extends ListenableFutureTask<T> {
141
142                private volatile Thread completingThread;
143
144                @SuppressWarnings("unchecked")
145                public SettableTask() {
146                        super((Callable<T>) DUMMY_CALLABLE);
147                }
148
149                public boolean setResultValue(T value) {
150                        set(value);
151                        return checkCompletingThread();
152                }
153
154                public boolean setExceptionResult(Throwable exception) {
155                        setException(exception);
156                        return checkCompletingThread();
157                }
158
159                @Override
160                protected void done() {
161                        if (!isCancelled()) {
162                                // Implicitly invoked by set/setException: store current thread for
163                                // determining whether the given result has actually triggered completion
164                                // (since FutureTask.set/setException unfortunately don't expose that)
165                                this.completingThread = Thread.currentThread();
166                        }
167                        super.done();
168                }
169
170                private boolean checkCompletingThread() {
171                        boolean check = (this.completingThread == Thread.currentThread());
172                        if (check) {
173                                this.completingThread = null;  // only first match actually counts
174                        }
175                        return check;
176                }
177        }
178
179}