Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / FutureHelper.java
blob0d218b234a249e6a2f26fba0e11d3f77ecdc2752
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;
13 /**
14 * Utilities for working with {@link Future Futures} in the synchronous
15 * datastore api.
18 @AppEngineInternal
19 public final class FutureHelper {
21 /**
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) {
34 try {
35 return getInternal(future);
36 } catch (ExecutionException e) {
37 throw propagateAsRuntimeException(e);
41 /**
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
57 * type is caught.
59 public static <T, E extends Exception> T quietGet(Future<T> future, Class<E> exceptionClass)
60 throws E {
61 try {
62 return getInternal(future);
63 } catch (ExecutionException e) {
64 if (e.getCause().getClass().equals(exceptionClass)) {
65 @SuppressWarnings("unchecked")
66 E exception = (E) e.getCause();
67 throw exception;
69 throw propagateAsRuntimeException(e);
73 private static <T> T getInternal(Future<T> future) throws ExecutionException {
74 try {
75 return future.get();
76 } catch (InterruptedException e) {
77 Thread.currentThread().interrupt();
78 throw new DatastoreFailureException("Unexpected failure", e);
82 /**
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
87 * convenience.
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();
94 } else {
95 throw new UndeclaredThrowableException(ee.getCause());
99 /**
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;
112 @Override
113 public boolean cancel(boolean mayInterruptIfRunning) {
114 boolean result = true;
115 for (Future<K> future : futures) {
116 result &= future.cancel(mayInterruptIfRunning);
118 return result;
121 @Override
122 public boolean isCancelled() {
123 boolean result = true;
124 for (Future<K> future : futures) {
125 result &= future.isCancelled();
127 return result;
130 @Override
131 public boolean isDone() {
132 boolean result = true;
133 for (Future<K> future : futures) {
134 result &= future.isDone();
136 return result;
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;
153 this.txn = txn;
154 this.txnStack = txnStack;
157 @Override
158 public boolean cancel(boolean mayInterruptIfRunning) {
159 return future.cancel(mayInterruptIfRunning);
162 @Override
163 public boolean isCancelled() {
164 return future.isCancelled();
167 @Override
168 public boolean isDone() {
169 return future.isDone();
172 @Override
173 public T get() throws InterruptedException, ExecutionException {
174 txnStack.getFutures(txn).remove(future);
175 return future.get();
178 @Override
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;
197 @Override
198 public boolean cancel(boolean mayInterruptIfRunning) {
199 return false;
202 @Override
203 public boolean isCancelled() {
204 return false;
207 @Override
208 public boolean isDone() {
209 return true;
212 @Override
213 @SuppressWarnings("unused")
214 public T get() throws ExecutionException {
215 return result;
218 @Override
219 public T get(long timeout, TimeUnit unit) {
220 return result;