App Engine Java SDK version 1.9.8
[gae.git] / java / src / main / com / google / appengine / api / utils / FutureWrapper.java
blob60bd11d4f4d1cbece48c3ac59b7057cd1d7d5e23
1 // Copyright 2009 Google Inc. All rights reserved.
3 package com.google.appengine.api.utils;
5 import com.google.common.base.Preconditions;
7 import java.util.concurrent.ExecutionException;
8 import java.util.concurrent.Future;
9 import java.util.concurrent.TimeUnit;
10 import java.util.concurrent.TimeoutException;
11 import java.util.concurrent.locks.Lock;
12 import java.util.concurrent.locks.ReentrantLock;
14 /**
15 * {@code FutureWrapper} is a simple {@link Future} that wraps a
16 * parent {@code Future}. This class is thread-safe.
18 * @param <K> The type of this {@link Future}
19 * @param <V> The type of the wrapped {@link Future}
21 public abstract class FutureWrapper<K, V> implements Future<V> {
23 private final Future<K> parent;
25 private boolean hasResult;
26 private V successResult;
27 private ExecutionException exceptionResult;
29 private final Lock lock = new ReentrantLock();
31 public FutureWrapper(Future<K> parent) {
32 this.parent = Preconditions.checkNotNull(parent);
35 @Override
36 public boolean cancel(boolean mayInterruptIfRunning) {
37 return parent.cancel(mayInterruptIfRunning);
40 @Override
41 public boolean isCancelled() {
42 return parent.isCancelled();
45 @Override
46 public boolean isDone() {
47 return parent.isDone();
50 private V handleParentException(Throwable cause) throws Throwable {
51 return setSuccessResult(absorbParentException(cause));
54 private V wrapAndCache(K data) throws Exception {
55 return setSuccessResult(wrap(data));
58 private V setSuccessResult(V result) {
59 successResult = result;
60 hasResult = true;
61 return result;
64 private ExecutionException setExceptionResult(Throwable ex) {
65 exceptionResult = new ExecutionException(ex);
66 hasResult = true;
67 return exceptionResult;
70 private V getCachedResult() throws ExecutionException {
71 if (exceptionResult != null) {
72 throw exceptionResult;
74 return successResult;
77 @Override
78 public V get() throws ExecutionException, InterruptedException {
79 lock.lock();
80 try {
81 if (hasResult) {
82 return getCachedResult();
85 try {
86 K value;
87 try {
88 value = parent.get();
89 } catch (ExecutionException ex) {
90 return handleParentException(ex.getCause());
92 return wrapAndCache(value);
93 } catch (InterruptedException ex) {
94 throw ex;
95 } catch (Throwable ex) {
96 throw setExceptionResult(convertException(ex));
98 } finally {
99 lock.unlock();
103 @Override
104 public V get(long timeout, TimeUnit unit)
105 throws InterruptedException, TimeoutException, ExecutionException {
106 long tryLockStart = System.currentTimeMillis();
107 if (!lock.tryLock(timeout, unit)) {
108 throw new TimeoutException();
110 try {
111 if (hasResult) {
112 return getCachedResult();
114 long remainingDeadline = TimeUnit.MILLISECONDS.convert(timeout, unit) -
115 (System.currentTimeMillis() - tryLockStart);
116 try {
117 K value;
118 try {
119 value = parent.get(remainingDeadline, TimeUnit.MILLISECONDS);
120 } catch (ExecutionException ex) {
121 return handleParentException(ex.getCause());
123 return wrapAndCache(value);
124 } catch (InterruptedException ex) {
125 throw ex;
126 } catch (TimeoutException ex) {
127 throw ex;
128 } catch (Throwable ex) {
129 throw setExceptionResult(convertException(ex));
131 } finally {
132 lock.unlock();
136 @Override
137 public final int hashCode() {
138 return super.hashCode();
141 @Override
142 public final boolean equals(Object obj) {
143 return super.equals(obj);
146 protected abstract V wrap(K key) throws Exception;
149 * Override this method if you want to suppress an exception thrown by the
150 * parent and return a value instead.
152 protected V absorbParentException(Throwable cause) throws Throwable {
153 throw cause;
156 protected abstract Throwable convertException(Throwable cause);