View Javadoc

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 }