1 /*
2 * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3 * the spring framework, http://el4j.sf.net
4 * Copyright (C) 2006 by ELCA Informatique SA, Av. de la Harpe 22-24,
5 * 1000 Lausanne, Switzerland, http://www.elca.ch
6 *
7 * EL4J is published under the GNU Lesser General Public License (LGPL)
8 * Version 2.1. See http://www.gnu.org/licenses/
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * For alternative licensing, please contact info@elca.ch
16 */
17 package ch.elca.el4j.util.observer.impl;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21
22 import ch.elca.el4j.util.observer.InquisitiveValueObserver;
23 import ch.elca.el4j.util.observer.ObservableValue;
24
25
26
27 /**
28 * A LiveValue is a derived value that stays up to date by itself.
29 *
30 * <p>A LiveValue is defined by a method computing it. The method is usually
31 * implemented in a {@link Computable} and given to LiveValue's public
32 * constructor. Alternatively, the method may also be provided by overriding
33 * {@link #is()}. In that case, you may <i>not</i> assume that the subclass'
34 * constructor is invoked before {@code is()} is.
35 *
36 * <p>A LiveValue is an ObservableValue. Therefore, it is possible to build
37 * directed acyclic graphs (and thus trees) of LiveValues. For
38 * obvious reasons, cyclic dependencies are not permitted; detection of one
39 * results in a {@link
40 * ch.elca.el4j.util.observer.impl.LiveValue.CyclicDependencyException
41 * CyclicDependencyException} to be thrown.
42 *
43 * <p>This class is thread safe.
44 *
45 * <p>If the live value only
46 * depends on values of type ObservableValue (which it obtains by invoking
47 * {@link ObservableValue#get()}), the live value will keep itself up to date
48 * by itself. Otherwise, updates must be requested manually using
49 * {@link #revise()}.
50 *
51 * @svnLink $Revision: 3880 $;$Date: 2009-08-04 15:17:52 +0200 (Di, 04. Aug 2009) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/core/src/main/java/ch/elca/el4j/util/observer/impl/LiveValue.java $
52 *
53 * @param <T> see supertype
54 * @see LiveValueFactory
55 * @author Adrian Moos (AMS)
56 */
57
58 // Implementation notes: By necessity, this class closely cooperates with
59 // ObservableValue, in particular with method get().
60 // By "local code",
61 // we therefore mean any code in this class or in one of these methods.
62 //
63 // this class is a bit involved because the methods need both thread safety and
64 // reentrance protection (Java's monitors are reentrant).
65
66 public class LiveValue<T> extends AbstractObservableValue<T>
67 implements InquisitiveValueObserver<Object> {
68
69 /** mutual exclusion for threads manipulating <code>m_currency</code>. */
70 static final Object s_dirtiesMonitor = new Object();
71
72 /** the live value currently beeing evaluated,
73 * or <code>null</code>, if there is no such live value.
74 */
75 static LiveValue<?> s_updating;
76
77 /** a cached value's degree of currency. */
78 protected enum Currency {
79 /** the cached value is outdated. */
80 dirty,
81
82 /** the cached value is currently beeing updated. */
83 updating,
84
85 /** the cached value is up to date. */
86 current
87 };
88
89 /** the cached value's currency. */
90 Currency m_currency;
91
92 /** the observables this live value currently depends on. */
93 Collection<ObservableValue<?>> m_dependencies
94 = java.util.Collections.emptyList();
95
96 /** the computation defining this live value. */
97 Computable<T> m_comp;
98
99
100 /** creates this live value. Subclasses must override {@link #is()}
101 * and be prepared to receive calls to it before their constructor is
102 * invoked. (This restriction can be circumvented by initializing the
103 * live value in a separate {@link Computable} before passing it to this
104 * class' other constructor)
105 */
106 protected LiveValue() {
107 super();
108 init();
109 }
110
111 /** creates the live value defined by the supplied {@link Computable}. */
112 public LiveValue(Computable<T> computation) {
113 m_comp = computation;
114 init();
115 }
116
117 /**
118 * The computation represented by this live value. Uses other observables
119 * and
120 * @return the live value's current value
121 */
122 protected T is() {
123 return m_comp.is();
124 }
125
126 // Liveness: If a Live Value is dirty, work() is in progress or
127 // ObservableValue.announce() is currently being executed. In either case
128 // work() is executed before control returns outside local code.
129
130 /** thrown to indicate that a cyclic dependency (=non-terminating
131 * recursion) among rules prevents their evaluation. */
132 static class CyclicDependencyException extends RuntimeException {
133 /** creates it. */
134 CyclicDependencyException(LiveValue<?> r) {
135 super(r.toString() + " depends on itself (directly or indirectly)");
136 }
137 }
138
139 /** updates the cached result. */
140 private void update() {
141 assert Thread.holdsLock(s_dirtiesMonitor);
142 switch (m_currency) {
143 case dirty:
144 for (ObservableValue<?> o : m_dependencies) {
145 o.unsubscribe(this);
146 }
147 m_dependencies = new ArrayList<ObservableValue<?>>();
148
149 LiveValue<?> previouslyUpdating = s_updating;
150 m_currency = Currency.updating;
151 s_updating = this;
152
153 T v = is();
154
155 m_currency = Currency.current;
156 s_updating = previouslyUpdating;
157
158 set(v);
159 break;
160 case updating:
161 assert false;
162 break;
163 case current:
164 break;
165 default:
166 assert false;
167 }
168 }
169
170 /** invoked by obs.get(). Note the currently updating live value's
171 * dependency.
172 */
173 static void observableGetterInterceptor(ObservableValue<?> obs) {
174 if (s_updating != null) {
175 assert Thread.holdsLock(s_dirtiesMonitor);
176 synchronized (s_dirtiesMonitor) {
177 obs.subscribeSilently(s_updating);
178 s_updating.m_dependencies.add(obs);
179 }
180 }
181 }
182
183 /** determines the initial result. */
184 private void init() {
185 synchronized (s_dirtiesMonitor) {
186 needsUpdate();
187 update();
188 }
189 }
190
191 /** marks this live value's cached result for updating. */
192 private void needsUpdate() {
193 assert Thread.holdsLock(s_dirtiesMonitor);
194 m_currency = Currency.dirty;
195 }
196
197 /** revise the result cached by this live value, i.e. ensure it is still
198 * correct.Invoke this method if non-observable input values may have
199 * changed.
200 * */
201 public void revise() {
202 synchronized (s_dirtiesMonitor) {
203 needsUpdate();
204 update();
205 }
206 }
207
208
209 /**
210 * {@inheritDoc }.
211 */
212 @Override
213 public T get() {
214 synchronized (s_dirtiesMonitor) {
215 switch (m_currency) {
216 case dirty:
217 update();
218 break;
219 case updating:
220 throw new CyclicDependencyException(this);
221 case current:
222 break;
223 default:
224 assert false;
225 }
226 assert m_currency == Currency.current;
227 return super.get();
228 }
229 }
230
231 /** not intended for public use. */
232 // callers must call work() before returning
233 public void changed(Object newvalue) {
234 synchronized (s_dirtiesMonitor) {
235 needsUpdate();
236 }
237 }
238
239 /** returns a textual description of this live value. */
240 public String toString() {
241 if (getClass().equals(LiveValue.class)) {
242 return "LiveValue defined by: " + m_comp.toString();
243 } else {
244 return getClass().getName();
245 }
246 }
247
248 /** not intended for public use. */
249 public void notified() {
250 synchronized (s_dirtiesMonitor) {
251 update();
252 }
253 }
254 }