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}