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
;
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
);
36 public boolean cancel(boolean mayInterruptIfRunning
) {
37 return parent
.cancel(mayInterruptIfRunning
);
41 public boolean isCancelled() {
42 return parent
.isCancelled();
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
;
64 private ExecutionException
setExceptionResult(Throwable ex
) {
65 exceptionResult
= new ExecutionException(ex
);
67 return exceptionResult
;
70 private V
getCachedResult() throws ExecutionException
{
71 if (exceptionResult
!= null) {
72 throw exceptionResult
;
78 public V
get() throws ExecutionException
, InterruptedException
{
82 return getCachedResult();
89 } catch (ExecutionException ex
) {
90 return handleParentException(ex
.getCause());
92 return wrapAndCache(value
);
93 } catch (InterruptedException ex
) {
95 } catch (Throwable ex
) {
96 throw setExceptionResult(convertException(ex
));
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();
112 return getCachedResult();
114 long remainingDeadline
= TimeUnit
.MILLISECONDS
.convert(timeout
, unit
) -
115 (System
.currentTimeMillis() - tryLockStart
);
119 value
= parent
.get(remainingDeadline
, TimeUnit
.MILLISECONDS
);
120 } catch (ExecutionException ex
) {
121 return handleParentException(ex
.getCause());
123 return wrapAndCache(value
);
124 } catch (InterruptedException ex
) {
126 } catch (TimeoutException ex
) {
128 } catch (Throwable ex
) {
129 throw setExceptionResult(convertException(ex
));
137 public final int hashCode() {
138 return super.hashCode();
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
{
156 protected abstract Throwable
convertException(Throwable cause
);