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