View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.foxinmy.weixin4j.util;
18  
19  import java.util.concurrent.Callable;
20  import java.util.concurrent.ExecutionException;
21  import java.util.concurrent.Future;
22  import java.util.concurrent.FutureTask;
23  import java.util.concurrent.TimeUnit;
24  import java.util.concurrent.TimeoutException;
25  import java.util.concurrent.atomic.AtomicReference;
26  
27  /**
28   * A {@link org.springframework.util.concurrent.ListenableFuture
29   * ListenableFuture} whose value can be set via {@link #set(Object)} or
30   * {@link #setException(Throwable)}. It may also be cancelled.
31   *
32   * <p>
33   * Inspired by {@code com.google.common.util.concurrent.SettableFuture}.
34   *
35   * @author Mattias Severson
36   * @author Rossen Stoyanchev
37   * @since 4.1
38   */
39  public class SettableFuture<T> implements Future<T> {
40  
41  	private final SettableTask<T> settableTask;
42  
43  	private final FutureTask<T> futureTask;
44  
45  	public SettableFuture() {
46  		this.settableTask = new SettableTask<T>();
47  		this.futureTask = new FutureTask<T>(this.settableTask);
48  	}
49  
50  	/**
51  	 * Set the value of this future. This method will return {@code true} if the
52  	 * value was set successfully, or {@code false} if the future has already
53  	 * been set or cancelled.
54  	 * 
55  	 * @param value
56  	 *            the value that will be set.
57  	 * @return {@code true} if the value was successfully set, else
58  	 *         {@code false}.
59  	 */
60  	public boolean set(T value) {
61  		boolean success = this.settableTask.setValue(value);
62  		if (success) {
63  			this.futureTask.run();
64  		}
65  		return success;
66  	}
67  
68  	/**
69  	 * Set the exception of this future. This method will return {@code true} if
70  	 * the exception was set successfully, or {@code false} if the future has
71  	 * already been set or cancelled.
72  	 * 
73  	 * @param exception
74  	 *            the value that will be set.
75  	 * @return {@code true} if the exception was successfully set, else
76  	 *         {@code false}.
77  	 */
78  	public boolean setException(Throwable exception) {
79  		boolean success = this.settableTask.setException(exception);
80  		if (success) {
81  			this.futureTask.run();
82  		}
83  		return success;
84  	}
85  
86  	@Override
87  	public boolean cancel(boolean mayInterruptIfRunning) {
88  		this.settableTask.setCancelled();
89  		boolean cancelled = this.futureTask.cancel(mayInterruptIfRunning);
90  		if (cancelled && mayInterruptIfRunning) {
91  			interruptTask();
92  		}
93  		return cancelled;
94  	}
95  
96  	@Override
97  	public boolean isCancelled() {
98  		return this.futureTask.isCancelled();
99  	}
100 
101 	@Override
102 	public boolean isDone() {
103 		return this.futureTask.isDone();
104 	}
105 
106 	/**
107 	 * Retrieve the value.
108 	 * <p>
109 	 * Will return the value if it has been set via {@link #set(Object)}, throw
110 	 * an {@link java.util.concurrent.ExecutionException} if it has been set via
111 	 * {@link #setException(Throwable)} or throw a
112 	 * {@link java.util.concurrent.CancellationException} if it has been
113 	 * cancelled.
114 	 * 
115 	 * @return The value associated with this future.
116 	 */
117 	@Override
118 	public T get() throws InterruptedException, ExecutionException {
119 		return this.futureTask.get();
120 	}
121 
122 	/**
123 	 * Retrieve the value.
124 	 * <p>
125 	 * Will return the value if it has been set via {@link #set(Object)}, throw
126 	 * an {@link java.util.concurrent.ExecutionException} if it has been set via
127 	 * {@link #setException(Throwable)} or throw a
128 	 * {@link java.util.concurrent.CancellationException} if it has been
129 	 * cancelled.
130 	 * 
131 	 * @param timeout
132 	 *            the maximum time to wait.
133 	 * @param unit
134 	 *            the time unit of the timeout argument.
135 	 * @return The value associated with this future.
136 	 */
137 	@Override
138 	public T get(long timeout, TimeUnit unit) throws InterruptedException,
139 			ExecutionException, TimeoutException {
140 		return this.futureTask.get(timeout, unit);
141 	}
142 
143 	/**
144 	 * Subclasses can override this method to implement interruption of the
145 	 * future's computation. The method is invoked automatically by a successful
146 	 * call to {@link #cancel(boolean) cancel(true)}.
147 	 * <p>
148 	 * The default implementation does nothing.
149 	 */
150 	protected void interruptTask() {
151 	}
152 
153 	private static class SettableTask<T> implements Callable<T> {
154 
155 		private static final String NO_VALUE = SettableFuture.class
156 				.getName() + ".NO_VALUE";
157 
158 		private final AtomicReference<Object> value = new AtomicReference<Object>(
159 				NO_VALUE);
160 
161 		private volatile boolean cancelled = false;
162 
163 		public boolean setValue(T value) {
164 			if (this.cancelled) {
165 				return false;
166 			}
167 			return this.value.compareAndSet(NO_VALUE, value);
168 		}
169 
170 		public boolean setException(Throwable exception) {
171 			if (this.cancelled) {
172 				return false;
173 			}
174 			return this.value.compareAndSet(NO_VALUE, exception);
175 		}
176 
177 		public void setCancelled() {
178 			this.cancelled = true;
179 		}
180 
181 		@SuppressWarnings("unchecked")
182 		@Override
183 		public T call() throws Exception {
184 			if (value.get() instanceof Exception) {
185 				throw (Exception) value.get();
186 			}
187 			return (T) value.get();
188 		}
189 	}
190 
191 }