1 // Copyright 2010 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.api
.datastore
;
5 import com
.google
.apphosting
.api
.AppEngineInternal
;
7 import java
.lang
.reflect
.UndeclaredThrowableException
;
8 import java
.util
.concurrent
.ExecutionException
;
9 import java
.util
.concurrent
.Future
;
10 import java
.util
.concurrent
.TimeUnit
;
11 import java
.util
.concurrent
.TimeoutException
;
14 * Utilities for working with {@link Future Futures} in the synchronous
19 public final class FutureHelper
{
22 * Return the result of the provided {@link Future}, converting all
23 * checked exceptions to unchecked exceptions so the caller doesn't have to
24 * handle them. If an {@link ExecutionException ExecutionException} is
25 * thrown the cause is wrapped in a {@link RuntimeException}. If an {@link
26 * InterruptedException} is thrown it is wrapped in a {@link
27 * DatastoreFailureException}.
29 * @param future The Future whose result we want to return.
30 * @param <T> The type of the provided Future.
31 * @return The result of the provided Future.
33 public static <T
> T
quietGet(Future
<T
> future
) {
35 return getInternal(future
);
36 } catch (ExecutionException e
) {
37 throw propagateAsRuntimeException(e
);
42 * Return the result of the provided {@link Future}, converting all
43 * checked exceptions except those of the provided type to unchecked
44 * exceptions so the caller doesn't have to handle them. If an {@link
45 * ExecutionException ExecutionException} is thrown and the type of the cause
46 * does not equal {@code exceptionClass} the cause is wrapped in a {@link
47 * RuntimeException}. If the type of the cause does equal {@code
48 * exceptionClass} the cause itself is thrown. If an {@link
49 * InterruptedException} is thrown it is wrapped in a {@link
50 * DatastoreFailureException}.
52 * @param future The Future whose result we want to return.
53 * @param <T> The type of the provided Future.
54 * @param exceptionClass Exceptions of this type will be rethrown.
55 * @return The result of the provided Future.
56 * @throws E Thrown If an ExecutionException with a cause of the appropriate
59 public static <T
, E
extends Exception
> T
quietGet(Future
<T
> future
, Class
<E
> exceptionClass
)
62 return getInternal(future
);
63 } catch (ExecutionException e
) {
64 if (e
.getCause().getClass().equals(exceptionClass
)) {
65 @SuppressWarnings("unchecked")
66 E exception
= (E
) e
.getCause();
69 throw propagateAsRuntimeException(e
);
73 private static <T
> T
getInternal(Future
<T
> future
) throws ExecutionException
{
76 } catch (InterruptedException e
) {
77 Thread
.currentThread().interrupt();
78 throw new DatastoreFailureException("Unexpected failure", e
);
83 * Propagates the {@code cause} of the given {@link ExecutionException}
84 * as a RuntimeException.
86 * @return nothing will ever be returned; this return type is only for
89 private static RuntimeException
propagateAsRuntimeException(ExecutionException ee
) {
90 if (ee
.getCause() instanceof RuntimeException
) {
91 throw (RuntimeException
) ee
.getCause();
92 } else if (ee
.getCause() instanceof Error
) {
93 throw (Error
) ee
.getCause();
95 throw new UndeclaredThrowableException(ee
.getCause());
100 * A base class for a {@link Future} that derives its result from multiple other futures.
102 * @param <K> The type used by sub-futures.
103 * @param <V> The type returned by this future.
105 abstract static class MultiFuture
<K
, V
> implements Future
<V
> {
106 protected final Iterable
<Future
<K
>> futures
;
108 public MultiFuture(Iterable
<Future
<K
>> futures
) {
109 this.futures
= futures
;
113 public boolean cancel(boolean mayInterruptIfRunning
) {
114 boolean result
= true;
115 for (Future
<K
> future
: futures
) {
116 result
&= future
.cancel(mayInterruptIfRunning
);
122 public boolean isCancelled() {
123 boolean result
= true;
124 for (Future
<K
> future
: futures
) {
125 result
&= future
.isCancelled();
131 public boolean isDone() {
132 boolean result
= true;
133 for (Future
<K
> future
: futures
) {
134 result
&= future
.isDone();
141 * We need a future implementation that clears out the txn callbacks when
142 * any variation of get() is called. This is important because if get()
143 * throws an exception, we don't want that exception to resurface when
144 * the txn gets committed or rolled back.
146 static final class TxnAwareFuture
<T
> implements Future
<T
> {
147 private final Future
<T
> future
;
148 private final Transaction txn
;
149 private final TransactionStack txnStack
;
151 TxnAwareFuture(Future
<T
> future
, Transaction txn
, TransactionStack txnStack
) {
152 this.future
= future
;
154 this.txnStack
= txnStack
;
158 public boolean cancel(boolean mayInterruptIfRunning
) {
159 return future
.cancel(mayInterruptIfRunning
);
163 public boolean isCancelled() {
164 return future
.isCancelled();
168 public boolean isDone() {
169 return future
.isDone();
173 public T
get() throws InterruptedException
, ExecutionException
{
174 txnStack
.getFutures(txn
).remove(future
);
179 public T
get(long timeout
, TimeUnit unit
)
180 throws InterruptedException
, ExecutionException
, TimeoutException
{
181 txnStack
.getFutures(txn
).remove(future
);
182 return future
.get(timeout
, unit
);
187 * Wraps an already-resolved result in a {@link Future}.
188 * @param <T> The type of the Future.
190 static class FakeFuture
<T
> implements Future
<T
> {
191 private final T result
;
193 FakeFuture(T result
) {
194 this.result
= result
;
198 public boolean cancel(boolean mayInterruptIfRunning
) {
203 public boolean isCancelled() {
208 public boolean isDone() {
213 @SuppressWarnings("unused")
214 public T
get() throws ExecutionException
{
219 public T
get(long timeout
, TimeUnit unit
) {