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.Callable; 020import java.util.concurrent.ExecutionException; 021import java.util.concurrent.TimeUnit; 022import java.util.concurrent.TimeoutException; 023 024import org.springframework.util.Assert; 025 026/** 027 * A {@link ListenableFuture} whose value can be set via {@link #set(Object)} 028 * or {@link #setException(Throwable)}. It may also get cancelled. 029 * 030 * <p>Inspired by {@code com.google.common.util.concurrent.SettableFuture}. 031 * 032 * @author Mattias Severson 033 * @author Rossen Stoyanchev 034 * @author Juergen Hoeller 035 * @since 4.1 036 */ 037public class SettableListenableFuture<T> implements ListenableFuture<T> { 038 039 private static final Callable<Object> DUMMY_CALLABLE = new Callable<Object>() { 040 @Override 041 public Object call() throws Exception { 042 throw new IllegalStateException("Should never be called"); 043 } 044 }; 045 046 047 private final SettableTask<T> settableTask = new SettableTask<T>(); 048 049 050 /** 051 * Set the value of this future. This method will return {@code true} if the 052 * value was set successfully, or {@code false} if the future has already been 053 * set or cancelled. 054 * @param value the value that will be set 055 * @return {@code true} if the value was successfully set, else {@code false} 056 */ 057 public boolean set(T value) { 058 return this.settableTask.setResultValue(value); 059 } 060 061 /** 062 * Set the exception of this future. This method will return {@code true} if the 063 * exception was set successfully, or {@code false} if the future has already been 064 * set or cancelled. 065 * @param exception the value that will be set 066 * @return {@code true} if the exception was successfully set, else {@code false} 067 */ 068 public boolean setException(Throwable exception) { 069 Assert.notNull(exception, "Exception must not be null"); 070 return this.settableTask.setExceptionResult(exception); 071 } 072 073 @Override 074 public void addCallback(ListenableFutureCallback<? super T> callback) { 075 this.settableTask.addCallback(callback); 076 } 077 078 @Override 079 public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) { 080 this.settableTask.addCallback(successCallback, failureCallback); 081 } 082 083 @Override 084 public boolean cancel(boolean mayInterruptIfRunning) { 085 boolean cancelled = this.settableTask.cancel(mayInterruptIfRunning); 086 if (cancelled && mayInterruptIfRunning) { 087 interruptTask(); 088 } 089 return cancelled; 090 } 091 092 @Override 093 public boolean isCancelled() { 094 return this.settableTask.isCancelled(); 095 } 096 097 @Override 098 public boolean isDone() { 099 return this.settableTask.isDone(); 100 } 101 102 /** 103 * Retrieve the value. 104 * <p>This method returns the value if it has been set via {@link #set(Object)}, 105 * throws an {@link java.util.concurrent.ExecutionException} if an exception has 106 * been set via {@link #setException(Throwable)}, or throws a 107 * {@link java.util.concurrent.CancellationException} if the future has been cancelled. 108 * @return the value associated with this future 109 */ 110 @Override 111 public T get() throws InterruptedException, ExecutionException { 112 return this.settableTask.get(); 113 } 114 115 /** 116 * Retrieve the value. 117 * <p>This method returns the value if it has been set via {@link #set(Object)}, 118 * throws an {@link java.util.concurrent.ExecutionException} if an exception has 119 * been set via {@link #setException(Throwable)}, or throws a 120 * {@link java.util.concurrent.CancellationException} if the future has been cancelled. 121 * @param timeout the maximum time to wait 122 * @param unit the unit of the timeout argument 123 * @return the value associated with this future 124 */ 125 @Override 126 public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 127 return this.settableTask.get(timeout, unit); 128 } 129 130 /** 131 * Subclasses can override this method to implement interruption of the future's 132 * computation. The method is invoked automatically by a successful call to 133 * {@link #cancel(boolean) cancel(true)}. 134 * <p>The default implementation is empty. 135 */ 136 protected void interruptTask() { 137 } 138 139 140 private static class SettableTask<T> extends ListenableFutureTask<T> { 141 142 private volatile Thread completingThread; 143 144 @SuppressWarnings("unchecked") 145 public SettableTask() { 146 super((Callable<T>) DUMMY_CALLABLE); 147 } 148 149 public boolean setResultValue(T value) { 150 set(value); 151 return checkCompletingThread(); 152 } 153 154 public boolean setExceptionResult(Throwable exception) { 155 setException(exception); 156 return checkCompletingThread(); 157 } 158 159 @Override 160 protected void done() { 161 if (!isCancelled()) { 162 // Implicitly invoked by set/setException: store current thread for 163 // determining whether the given result has actually triggered completion 164 // (since FutureTask.set/setException unfortunately don't expose that) 165 this.completingThread = Thread.currentThread(); 166 } 167 super.done(); 168 } 169 170 private boolean checkCompletingThread() { 171 boolean check = (this.completingThread == Thread.currentThread()); 172 if (check) { 173 this.completingThread = null; // only first match actually counts 174 } 175 return check; 176 } 177 } 178 179}