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.ExecutionException;
020import java.util.concurrent.Future;
021import java.util.concurrent.TimeUnit;
022import java.util.concurrent.TimeoutException;
023
024import org.springframework.lang.Nullable;
025import org.springframework.util.Assert;
026
027/**
028 * Abstract class that adapts a {@link Future} parameterized over S into a {@code Future}
029 * parameterized over T. All methods are delegated to the adaptee, where {@link #get()}
030 * and {@link #get(long, TimeUnit)} call {@link #adapt(Object)} on the adaptee's result.
031 *
032 * @author Arjen Poutsma
033 * @since 4.0
034 * @param <T> the type of this {@code Future}
035 * @param <S> the type of the adaptee's {@code Future}
036 */
037public abstract class FutureAdapter<T, S> implements Future<T> {
038
039        private final Future<S> adaptee;
040
041        @Nullable
042        private Object result;
043
044        private State state = State.NEW;
045
046        private final Object mutex = new Object();
047
048
049        /**
050         * Constructs a new {@code FutureAdapter} with the given adaptee.
051         * @param adaptee the future to delegate to
052         */
053        protected FutureAdapter(Future<S> adaptee) {
054                Assert.notNull(adaptee, "Delegate must not be null");
055                this.adaptee = adaptee;
056        }
057
058
059        /**
060         * Returns the adaptee.
061         */
062        protected Future<S> getAdaptee() {
063                return this.adaptee;
064        }
065
066        @Override
067        public boolean cancel(boolean mayInterruptIfRunning) {
068                return this.adaptee.cancel(mayInterruptIfRunning);
069        }
070
071        @Override
072        public boolean isCancelled() {
073                return this.adaptee.isCancelled();
074        }
075
076        @Override
077        public boolean isDone() {
078                return this.adaptee.isDone();
079        }
080
081        @Override
082        @Nullable
083        public T get() throws InterruptedException, ExecutionException {
084                return adaptInternal(this.adaptee.get());
085        }
086
087        @Override
088        @Nullable
089        public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
090                return adaptInternal(this.adaptee.get(timeout, unit));
091        }
092
093        @SuppressWarnings("unchecked")
094        @Nullable
095        final T adaptInternal(S adapteeResult) throws ExecutionException {
096                synchronized (this.mutex) {
097                        switch (this.state) {
098                                case SUCCESS:
099                                        return (T) this.result;
100                                case FAILURE:
101                                        Assert.state(this.result instanceof ExecutionException, "Failure without exception");
102                                        throw (ExecutionException) this.result;
103                                case NEW:
104                                        try {
105                                                T adapted = adapt(adapteeResult);
106                                                this.result = adapted;
107                                                this.state = State.SUCCESS;
108                                                return adapted;
109                                        }
110                                        catch (ExecutionException ex) {
111                                                this.result = ex;
112                                                this.state = State.FAILURE;
113                                                throw ex;
114                                        }
115                                        catch (Throwable ex) {
116                                                ExecutionException execEx = new ExecutionException(ex);
117                                                this.result = execEx;
118                                                this.state = State.FAILURE;
119                                                throw execEx;
120                                        }
121                                default:
122                                        throw new IllegalStateException();
123                        }
124                }
125        }
126
127        /**
128         * Adapts the given adaptee's result into T.
129         * @return the adapted result
130         */
131        @Nullable
132        protected abstract T adapt(S adapteeResult) throws ExecutionException;
133
134
135        private enum State {NEW, SUCCESS, FAILURE}
136
137}