001/*
002 * Copyright 2002-2018 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.scheduling.annotation;
018
019import java.util.concurrent.CompletableFuture;
020import java.util.concurrent.ExecutionException;
021import java.util.concurrent.Future;
022import java.util.concurrent.TimeUnit;
023
024import org.springframework.lang.Nullable;
025import org.springframework.util.concurrent.FailureCallback;
026import org.springframework.util.concurrent.ListenableFuture;
027import org.springframework.util.concurrent.ListenableFutureCallback;
028import org.springframework.util.concurrent.SuccessCallback;
029
030/**
031 * A pass-through {@code Future} handle that can be used for method signatures
032 * which are declared with a {@code Future} return type for asynchronous execution.
033 *
034 * <p>As of Spring 4.1, this class implements {@link ListenableFuture}, not just
035 * plain {@link java.util.concurrent.Future}, along with the corresponding support
036 * in {@code @Async} processing.
037 *
038 * <p>As of Spring 4.2, this class also supports passing execution exceptions back
039 * to the caller.
040 *
041 * @author Juergen Hoeller
042 * @author Rossen Stoyanchev
043 * @since 3.0
044 * @param <V> the value type
045 * @see Async
046 * @see #forValue(Object)
047 * @see #forExecutionException(Throwable)
048 */
049public class AsyncResult<V> implements ListenableFuture<V> {
050
051        @Nullable
052        private final V value;
053
054        @Nullable
055        private final Throwable executionException;
056
057
058        /**
059         * Create a new AsyncResult holder.
060         * @param value the value to pass through
061         */
062        public AsyncResult(@Nullable V value) {
063                this(value, null);
064        }
065
066        /**
067         * Create a new AsyncResult holder.
068         * @param value the value to pass through
069         */
070        private AsyncResult(@Nullable V value, @Nullable Throwable ex) {
071                this.value = value;
072                this.executionException = ex;
073        }
074
075
076        @Override
077        public boolean cancel(boolean mayInterruptIfRunning) {
078                return false;
079        }
080
081        @Override
082        public boolean isCancelled() {
083                return false;
084        }
085
086        @Override
087        public boolean isDone() {
088                return true;
089        }
090
091        @Override
092        @Nullable
093        public V get() throws ExecutionException {
094                if (this.executionException != null) {
095                        throw (this.executionException instanceof ExecutionException ?
096                                        (ExecutionException) this.executionException :
097                                        new ExecutionException(this.executionException));
098                }
099                return this.value;
100        }
101
102        @Override
103        @Nullable
104        public V get(long timeout, TimeUnit unit) throws ExecutionException {
105                return get();
106        }
107
108        @Override
109        public void addCallback(ListenableFutureCallback<? super V> callback) {
110                addCallback(callback, callback);
111        }
112
113        @Override
114        public void addCallback(SuccessCallback<? super V> successCallback, FailureCallback failureCallback) {
115                try {
116                        if (this.executionException != null) {
117                                failureCallback.onFailure(exposedException(this.executionException));
118                        }
119                        else {
120                                successCallback.onSuccess(this.value);
121                        }
122                }
123                catch (Throwable ex) {
124                        // Ignore
125                }
126        }
127
128        @Override
129        public CompletableFuture<V> completable() {
130                if (this.executionException != null) {
131                        CompletableFuture<V> completable = new CompletableFuture<>();
132                        completable.completeExceptionally(exposedException(this.executionException));
133                        return completable;
134                }
135                else {
136                        return CompletableFuture.completedFuture(this.value);
137                }
138        }
139
140
141        /**
142         * Create a new async result which exposes the given value from {@link Future#get()}.
143         * @param value the value to expose
144         * @since 4.2
145         * @see Future#get()
146         */
147        public static <V> ListenableFuture<V> forValue(V value) {
148                return new AsyncResult<>(value, null);
149        }
150
151        /**
152         * Create a new async result which exposes the given exception as an
153         * {@link ExecutionException} from {@link Future#get()}.
154         * @param ex the exception to expose (either an pre-built {@link ExecutionException}
155         * or a cause to be wrapped in an {@link ExecutionException})
156         * @since 4.2
157         * @see ExecutionException
158         */
159        public static <V> ListenableFuture<V> forExecutionException(Throwable ex) {
160                return new AsyncResult<>(null, ex);
161        }
162
163        /**
164         * Determine the exposed exception: either the cause of a given
165         * {@link ExecutionException}, or the original exception as-is.
166         * @param original the original as given to {@link #forExecutionException}
167         * @return the exposed exception
168         */
169        private static Throwable exposedException(Throwable original) {
170                if (original instanceof ExecutionException) {
171                        Throwable cause = original.getCause();
172                        if (cause != null) {
173                                return cause;
174                        }
175                }
176                return original;
177        }
178
179}