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}