(no commit message)
[utils] / support / general / src / main / java / org / wamblee / cache / ComputedValue.java
1 /*
2  * Copyright 2005-2010 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 package org.wamblee.cache;
17
18 /**
19  * Utility class to deal with recomputation of a certain value. The goal is to
20  * have only one thread at a time compute the value while other threads that
21  * simulateneously detect recomputation continue with the old value.
22  * 
23  * 
24  * @author Erik Brakkee
25  * 
26  * @param <T>
27  */
28 public class ComputedValue<T> {
29
30     /**
31      * Computation
32      * 
33      * @param <T>
34      *            Type of object to compute.
35      */
36     public static interface Computation<T> {
37         /**
38          * Checks whether the object is out of date. This will be invoked while
39          * holding the lock passed at construction of the compute guard.
40          * 
41          * Any runtime exceptions thrown are passed back through the
42          * {@link ComputedValue#get()}.
43          * 
44          * @return True iff recomputation is necessary.
45          */
46         boolean isOutOfDate();
47
48         /**
49          * Computes the object. This will be invoked while <em>not</em> holding
50          * the lock passed at construction of the compute guard. It is
51          * guaranteed that per ComputeGuard, no concurrent calls to compute()
52          * are done.
53          * 
54          * Any runtime exceptions thrown are passed back through the
55          * {@link ComputedValue#get()}.
56          * 
57          * @return Computed object.
58          */
59         T compute();
60     }
61
62     private Object lock;
63     private Computation<T> computedValue;
64
65     private Boolean busy;
66     private T value;
67
68     /**
69      * Constructs the compute guard
70      * 
71      * @param aLock
72      *            Lock to use during computation and to guard the value.
73      * @param aComputation
74      *            Computation to use.
75      */
76     public ComputedValue(Object aLock, Computation aComputation) {
77         lock = aLock;
78         computedValue = aComputation;
79         busy = false;
80         value = null;
81     }
82
83     /**
84      * Triggers computation of the value (if no other thread is currently
85      * computing the value).
86      */
87     public void compute() {
88         synchronized (this) {
89             if (busy) {
90                 return; // another thread is already taking care of it.
91             }
92             busy = true;
93         }
94         try {
95             T newvalue = computedValue.compute();
96             set(newvalue);
97         } finally {
98             synchronized (this) {
99                 busy = false;
100             }
101         }
102     }
103
104     /**
105      * Gets the current value of the object, recomputing it if the object is out
106      * of date. This method ensures that only one thread at a time will do
107      * recomputations.
108      * 
109      * @return Current value.
110      */
111     public T get() {
112         boolean mustCompute = false;
113         synchronized (lock) {
114             if (computedValue.isOutOfDate()) {
115                 mustCompute = true;
116             }
117         }
118         if (mustCompute) {
119             compute();
120         }
121         synchronized (lock) {
122             return value;
123         }
124     }
125
126     /**
127      * Gets the currently cached value. No recomputation is performed.
128      * 
129      * @return Cached value.
130      */
131     public T getCached() {
132         synchronized (lock) {
133             return value;
134         }
135
136     }
137
138     /**
139      * Sets the value explicitly.
140      * 
141      * @param aValue
142      *            value to set.
143      */
144     public void set(T aValue) {
145         synchronized (lock) {
146             value = aValue;
147         }
148     }
149 }