001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package threaddemo.util;
043:
044: import java.lang.ref.Reference;
045: import java.lang.ref.ReferenceQueue;
046: import java.lang.ref.WeakReference;
047: import java.lang.reflect.InvocationTargetException;
048: import java.util.ArrayList;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Map;
052: import java.util.SortedSet;
053: import java.util.TreeSet;
054: import java.util.WeakHashMap;
055: import java.util.logging.Level;
056: import java.util.logging.Logger;
057: import threaddemo.locking.RWLock;
058:
059: // XXX possibly need some method which is like getStaleValueNonBlocking but which
060: // throws InvocationTargetException, to permit views to display an error marker
061: // on stale data
062: // XXX might want an optional ability to take a snapshot of data in a read lock
063: // and then parse it outside the lock to avoid shutting out writers for too long
064:
065: /**
066: * Support for bidirectional construction of a derived model from an underlying model.
067: * Based on a lock which is assumed to control both models.
068: * Handles all locking and scheduling associated with such a system.
069: * It is possible to "nest" supports so that the derived model of one is the
070: * underlying model of another - but they must still share a common lock.
071: *
072: * <p>"Derive" means to take the underlying model (not represented explicitly here,
073: * but assumed to be "owned" by the subclass) and produce the derived model;
074: * typically this will involve parsing or the like. This operates in a read lock.
075: *
076: * <p>"Recreate" means to take a new derived model (which may in fact be the same
077: * as the old derived model but with different structure) and somehow change the
078: * underlying model on that basis.
079: *
080: * <p>"Initiate" means to start derivation asynchronously, not waiting for the
081: * result to be complete; this operation is idempotent, i.e. you can call it
082: * whenever you think you might like the value later, but it will not cause
083: * gratuitous extra derivations.
084: *
085: * <p>"Invalidate" means to signal that the underlying model has somehow changed
086: * and that if there is any derived model it should be considered stale.
087: * Invalidating when there is not yet any derived model is a no-op.
088: *
089: * <p>There are four different kinds of "values" which are employed by this class
090: * and which you should be careful to differentiate:
091: *
092: * <ol>
093: *
094: * <li><p>The state of the underlying model. This is <em>not</em> explicitly modeled
095: * by this class. Subclasses are expected to use that state as needed in
096: * {@link #doDerive} and {@link #doRecreate}.
097: *
098: * <li><p>The state ("value") of the derived model. This is never null and is the
099: * return value of {@link #doRecreate}, {@link #getValueBlocking},
100: * {@link #getValueNonBlocking}, and {@link #getStaleValueNonBlocking} (except
101: * where those methods are documented to return null), as well as the first
102: * parameter to {@link #doRecreate}, {@link #doDerive}, and
103: * {@link TwoWaySupport.DerivationResult#TwoWaySupport.DerivationResult}
104: * and the parameter to {@link #createReference}.
105: *
106: * <li><p>Deltas in the underlying model. These may in fact be entire new copies
107: * of an underlying model, or some diff-like structure, or an {@link java.util.EventObject},
108: * etc. - whatever seems most convenient. These are never null and are the argument
109: * type of {@link #invalidate} and the second argument type of {@link #doDerive}
110: * as well as both argument types and the return value of {@link #composeUnderlyingDeltas}.
111: *
112: * <li><p>Deltas in the derived model. Again these may be of the same form as the
113: * derived model itself - just replacing the model wholesale - or they may be some
114: * kind of diff or event structure. These are again never null and are the argument
115: * for {@link #mutate} and the second argument for {@link #doRecreate} and
116: * {@link TwoWaySupport.DerivationResult#TwoWaySupport.DerivationResult}.
117: *
118: * </ol>
119: *
120: * <p>Setting a new derived value explicitly always sets it immediately.
121: * When getting the derived value, you have several choices. You can ask for the
122: * exact value, if necessary waiting for it to be derived for the first time, or
123: * rederived if it is stale. Or you can ask for the value if it is fresh or accept
124: * null if it is missing or stale. Or you can ask for the value if it is fresh or
125: * stale and accept null if it is missing. The latter two operations do not block
126: * (except to get the read lock) and so are valuable in views.
127: *
128: * <p>Derivation is started immediately after an initiate operation if there is
129: * no derived model yet. If there is a model but it is stale and you ask to
130: * initiate derivation, by default this also starts immediately, but you may
131: * instead give a delay before the new derivation starts (assuming no one asks
132: * for the exact derived value before then); this is useful for cases where
133: * derivation is time-consuming (e.g. a complex parse) and for performance
134: * reasons you wish to avoid triggering it too frivolously. For example, you may
135: * be invalidating the derived model after every keystroke which changes a text
136: * document, but would prefer to wait a few seconds before showing new results.
137: *
138: * <p>In case a recreate operation is attempted during a delay in which the model
139: * is stale, or simply while a derivation is in progress with or without a preceding
140: * delay, there is a conflict: the recreated model is probably a modification of
141: * the old stale underlying model, and it is likely that setting it as the new derived
142: * model and recreating the underlying model would clobber intermediate changes in the
143: * underlying model, causing data loss. By default this support will signal an exception
144: * if this is attempted, though subclasses may choose to suppress that and forcibly
145: * set the new derived model and recreate the underlying model. Subclasses are better advised
146: * to use the exception, and ensure that views of the derived model either handle
147: * it gracefully (e.g. offering the user an opportunity to retry the modification
148: * on the new derived model when it is available, or just beeping), or put the
149: * derived view into a read-only mode temporarily while there is a stale underlying
150: * model so that such a situation cannot arise.
151: *
152: * <p>There is a kind of "external clobbering" that can occur if the view does not
153: * update itself promptly after a recreation (generally, after a change in the
154: * derived model leading to a fresh value) but only with some kind of delay. In
155: * that case an attempted change to the derived model may be working with obsolete
156: * data. The support does <em>not</em> try to handle this case; the view is
157: * responsible for detecting it and reacting appropriately.
158: *
159: * <p>Another kind of "clobbering" can occur in case the underlying model is not
160: * completely controlled by the lock. For example, it might be the native filesystem,
161: * which can change at any time without acquiring a lock in the JVM. In that case
162: * an attempted mutation may be operating against a model derived from an older
163: * state of the underlying model. Again, this support does <em>not</em> provide a
164: * solution for this problem. Subclasses should attempt to detect such a condition
165: * and recover from it gracefully, e.g. by throwing an exception from
166: * <code>doRecreate</code> or by merging changes. Using TwoWaySupport may not be
167: * appropriate for such cases anyway, since derivation could then cause an existing
168: * reader to see state changes within its read lock, which could violate its
169: * assumptions about the underlying model.
170: *
171: * <p>Derivation and recreation may throw checked exceptions. In such cases the
172: * underlying and derived models should be left in a consistent state if at all
173: * possible. If derivation throws an exception, the derived model will be considered
174: * stale, but no attempt to rederive the model will be made unless the underlying
175: * model is invalidated; subsequent calls to {@link #getValueBlocking} with the
176: * same underlying model will result in the same exception being thrown repeatedly.
177: * Views should generally put themselves into a read-only mode in this case.
178: * If recreation throws an exception, this is propagated to {@link #mutate} but
179: * otherwise nothing is changed.
180: *
181: * <p>You may not call any methods of this class from within the dynamic scope of
182: * {@link #doDerive} or {@link #doRecreate} or a listener callback.
183: *
184: * <p>You can attach a listener to this class. You will get an event when the
185: * status of the support changes. All events are fired as soon as possible in the
186: * read lock.
187: *
188: * @author Jesse Glick
189: */
190: public abstract class TwoWaySupport<DM, UMD, DMD> {
191:
192: /** logging support */
193: private static final Logger logger = Logger
194: .getLogger(TwoWaySupport.class.getName());
195:
196: /** lock used for all static vars */
197: private static final Object LOCK = new String("TwoWaySupport");
198:
199: /** supports which are scheduled to be derived but haven't been yet */
200: private static final SortedSet<DeriveTask> toDerive = new TreeSet<DeriveTask>();
201:
202: /** derivation tasks indexed by support */
203: private static final Map<TwoWaySupport, DeriveTask> tasks = new WeakHashMap<TwoWaySupport, DeriveTask>();
204:
205: /** derivation thread when it has been started */
206: private static boolean startedThread = false;
207:
208: /** queue of derived model references */
209: private static ReferenceQueue queue = null;
210:
211: /** reverse lookup for model field to support queue collector */
212: private static final Map<Reference<Object>, Reference<TwoWaySupport>> referencesToSupports = new WeakHashMap<Reference<Object>, Reference<TwoWaySupport>>();
213:
214: /** associated lock */
215: private final RWLock lock;
216:
217: /** listener list */
218: private final List<TwoWayListener<DM, UMD, DMD>> listeners;
219:
220: /** current derived model, if any */
221: private Reference<DM> model = null;
222:
223: /** current derivation problem, if any */
224: private Exception problem = null; // XXX should perhaps be Reference<Exception>?
225:
226: /** if model is not null, whether it is fresh or stale */
227: private boolean fresh = false;
228:
229: /** if true, derivation has been initiated */
230: private boolean active = false;
231:
232: /** underlying delta, if one is being processed thru initiate + doDerive */
233: private UMD underlyingDelta = null;
234:
235: /** currently in doRecreate() */
236: private boolean mutating = false;
237:
238: /** currently in doDerive() */
239: private boolean deriving = false;
240:
241: /**
242: * Create an uninitialized support.
243: * No derivation or recreation is scheduled initially.
244: * @param lock the associated lock
245: */
246: protected TwoWaySupport(RWLock lock) {
247: this .lock = lock;
248: listeners = new ArrayList<TwoWayListener<DM, UMD, DMD>>();
249: }
250:
251: /**
252: * Get the associated lock.
253: * @return the lock
254: */
255: public final RWLock getLock() {
256: return lock;
257: }
258:
259: /**
260: * Compute the derived model from the underlying model.
261: *
262: * <p>This method is called with a read lock held on the lock.
263: * However for derived models with mutable state you may need to acquire an
264: * additional simple lock (monitor) on some part of the model to refresh its
265: * state - this is not a true write, but other readers should be locked out
266: * until it is finished. For purely functional derived models that are
267: * replaced wholesale, this is not necessary.
268: *
269: * <p>Note that derivations never run in parallel, even though they are in a
270: * read lock. In this implementation, all derivations in fact run in a dedicated
271: * thread if they are invoked asynchronously using {@link #initiate}, but that
272: * may change.
273: *
274: * <p>{@link TwoWayListener#derived} will be triggered after this method
275: * completes. An implementation is responsible for notifying relevant listeners
276: * of changes to the derived model, but should not do so from within the scope
277: * of this method, as the new value of the derived model will not yet be available;
278: * instead listen for {@link TwoWayEvent.Derived}. Both the derived delta and
279: * final value are made available to that event for this reason.
280: *
281: * @param oldValue the old value of the derived model, or null if it had
282: * never been calculated before
283: * @param underlyingDelta a change in the underlying model, or null if no
284: * particular change was signalled
285: * @return the new value of the derived model (might be the same object as
286: * the old value) plus the derived delta
287: * @throws Exception (checked only!) if derivation of the model failed
288: */
289: protected abstract DerivationResult<DM, DMD> doDerive(DM oldValue,
290: UMD underlyingDelta) throws Exception;
291:
292: /**
293: * Result of a derivation. Includes both the final resulting value, and the
294: * derived delta. The derived delta is not used by {@link TwoWaySupport} except
295: * to pass to {@link TwoWayEvent.Derived}, which may be useful for subclasses
296: * firing changes.
297: */
298: protected final static class DerivationResult<DM, DMD> {
299: final DM newValue;
300: final DMD derivedDelta;
301:
302: /**
303: * Create a derivation result wrapper object.
304: * @param newValue the new value of the derived model
305: * @param derivedDelta some representation of the difference from the old
306: * model; must be null if the old derived value was null,
307: * and only then
308: */
309: public DerivationResult(DM newValue, DMD derivedDelta) {
310: if (newValue == null)
311: throw new NullPointerException();
312: this .newValue = newValue;
313: this .derivedDelta = derivedDelta;
314: }
315: }
316:
317: /**
318: * Compute the effect of two sequential changes to the underlying model.
319: *
320: * <p>This method is called with a read lock held on the lock.
321: *
322: * <p>After this method is called, the first argument is discarded by this support,
323: * so a subclass may implement it by mutating the first argument and returning it.
324: *
325: * @param underlyingDelta1 the older delta
326: * @param underlyingDelta2 the newer delta
327: * @return a delta representing those two changes applied in sequence
328: */
329: protected abstract UMD composeUnderlyingDeltas(
330: UMD underlyingDelta1, UMD underlyingDelta2);
331:
332: /**
333: * Recreate the underlying model from the derived model.
334: *
335: * <p>This method is called with a write lock held on the lock.
336: *
337: * <p>It is expected that any changes to the underlying model will be notified
338: * to the relevant listeners within the dynamic scope of this method. An implementation
339: * is responsible for notifying relevant listeners of changes to the derived
340: * model, but should not do so from within the scope of this method, as the
341: * new value of the derived model will not yet be available; instead listen for
342: * {@link TwoWayEvent.Recreated}.
343: *
344: * @param oldValue the old value of the derived model, or null if it was
345: * never derived
346: * @param derivedDelta a change in the derived model
347: * @return the new value of the derived model (might be the same object as
348: * the old value)
349: * @throws Exception (checked only!) if recreation of the underlying model failed
350: */
351: protected abstract DM doRecreate(DM oldValue, DMD derivedDelta)
352: throws Exception;
353:
354: private void assertStateConsistent() {
355: assert Thread.holdsLock(LOCK);
356: assert !fresh || model != null;
357: assert !fresh || problem == null;
358: assert !fresh || !active;
359: if (active) {
360: assert tasks.containsKey(this );
361: // XXX check that toDerive and tasks are consistent
362: } else {
363: assert !tasks.containsKey(this );
364: }
365: // XXX what else?
366: }
367:
368: /**
369: * Get the value of the derived model, blocking as needed until it is ready.
370: * This method requires the read lock and may block further for
371: * {@link #doDerive}.
372: * @return the value of the derived model (never null)
373: * @throws InvocationTargetException if <code>doDerive</code> was called
374: * and threw an exception (possibly from an
375: * earlier derivation run that is still broken)
376: */
377: public final DM getValueBlocking() throws InvocationTargetException {
378: assert lock.canRead();
379: DM old;
380: synchronized (LOCK) {
381: assertStateConsistent();
382: assert !mutating;
383: while (deriving) {
384: // Another reader is getting the value at the moment, wait for it.
385: logger
386: .finer("waiting for another reader to finish deriving");
387: try {
388: LOCK.wait();
389: } catch (InterruptedException e) {/* OK */
390: }
391: }
392: if (fresh) {
393: DM o = model.get();
394: if (o != null) {
395: logger.log(Level.FINER, "fresh value: {0}", o);
396: return o;
397: }
398: } else if (problem != null) {
399: logger.log(Level.FINER, "problem: {0}", problem);
400: deactivate();
401: throw new InvocationTargetExceptionNoStackTrace(problem);
402: }
403: // Else we need to block for a value.
404: old = (model != null) ? model.get() : null;
405: deriving = true;
406: fresh = false;
407: }
408: // Getting the value:
409: DerivationResult<DM, DMD> result;
410: DM newValue = null;
411: try {
412: result = doDerive(old, null);
413: if (result == null) {
414: throw new NullPointerException();
415: }
416: assert result.newValue != null;
417: if (old == null && result.derivedDelta != null) {
418: throw new IllegalStateException(
419: "Cannot have a non-null derivedDelta for a null oldValue");
420: }
421: if (old != null && result.derivedDelta == null) {
422: throw new IllegalStateException(
423: "Cannot have a null derivedDelta for a non-null oldValue");
424: }
425: logger.log(Level.FINER, "derived value: {0}",
426: result.newValue);
427: fresh = true;
428: newValue = result.newValue;
429: } catch (RuntimeException e) {
430: // We don't treat these as model-visible exceptions.
431: throw e;
432: } catch (Exception e) {
433: problem = e;
434: fresh = false;
435: fireChange(new TwoWayEvent.Broken<DM, UMD, DMD>(this , old,
436: underlyingDelta, e));
437: throw new InvocationTargetException(e);
438: } finally {
439: synchronized (LOCK) {
440: deriving = false;
441: LOCK.notifyAll();
442: if (newValue != null) {
443: setModel(newValue);
444: }
445: deactivate();
446: }
447: }
448: fireChange(new TwoWayEvent.Derived<DM, UMD, DMD>(this , old,
449: result.newValue, result.derivedDelta, underlyingDelta));
450: return result.newValue;
451: }
452:
453: private void deactivate() {
454: assert Thread.holdsLock(LOCK);
455: if (active) {
456: // No longer need to run this.
457: active = false;
458: DeriveTask t = tasks.remove(this );
459: assert t != null;
460: toDerive.remove(t);
461: }
462: }
463:
464: private static final class InvocationTargetExceptionNoStackTrace
465: extends InvocationTargetException {
466: public InvocationTargetExceptionNoStackTrace(Throwable problem) {
467: super (problem);
468: }
469:
470: public Throwable fillInStackTrace() {
471: return this ;
472: }
473: }
474:
475: private void setModel(DM result) {
476: assert Thread.holdsLock(LOCK);
477: assert result != null;
478: if (model != null) {
479: referencesToSupports.remove(model);
480: }
481: model = createEnqueuedReference(result);
482: @SuppressWarnings("unchecked")
483: Reference<Object> _model = (Reference<Object>) model;
484: referencesToSupports.put(_model,
485: new WeakReference<TwoWaySupport>(this ));
486: }
487:
488: /**
489: * Get the value of the derived model, if it is ready and fresh.
490: * This method requires the read lock but otherwise does not block.
491: * @return the value of the derived model, or null if it is stale or has never
492: * been computed at all
493: */
494: public final DM getValueNonBlocking() {
495: assert lock.canRead();
496: synchronized (LOCK) {
497: assertStateConsistent();
498: assert !mutating;
499: return fresh ? model.get() : null;
500: }
501: }
502:
503: /**
504: * Get the value of the derived model, if it is ready (fresh or stale).
505: * This method requires the read lock but otherwise does not block.
506: * @return the value of the derived model, or null if it has never been
507: * computed at all
508: */
509: public final DM getStaleValueNonBlocking() {
510: assert lock.canRead();
511: synchronized (LOCK) {
512: assertStateConsistent();
513: assert !mutating;
514: return (model != null) ? model.get() : null;
515: }
516: }
517:
518: /**
519: * Change the value of the derived model and correspondingly update the
520: * underlying model.
521: * <p>This method requires the write lock and calls {@link #doRecreate}
522: * if it does not throw <code>ClobberException</code>.
523: * @param derivedDelta a change to the derived model
524: * @return the new value of the derived model
525: * @throws ClobberException in case {@link #permitsClobbering} is false and
526: * the old value of the derived model was stale or
527: * missing
528: * @throws InvocationTargetException if <code>doRecreate</code> throws an
529: * exception
530: */
531: public final DM mutate(DMD derivedDelta) throws ClobberException,
532: InvocationTargetException {
533: if (derivedDelta == null)
534: throw new NullPointerException();
535: assert lock.canWrite();
536: DM oldValue;
537: synchronized (LOCK) {
538: assertStateConsistent();
539: assert !mutating;
540: assert !deriving;
541: oldValue = (model != null) ? model.get() : null;
542: if (!fresh && !permitsClobbering()) {
543: throw new ClobberException(this , oldValue, derivedDelta);
544: }
545: mutating = true;
546: }
547: DM result = null;
548: try {
549: // XXX should also dequeue if necessary to avoid sequence:
550: // invalidate -> initiate -> [pause] -> mutate -> [pause] -> invalidate -> [pause] -> derive
551: // where the final derivation was not really appropriate (or was it?)
552: result = doRecreate(oldValue, derivedDelta);
553: assert result != null;
554: } catch (RuntimeException e) {
555: throw e;
556: } catch (Exception e) {
557: throw new InvocationTargetException(e);
558: } finally {
559: synchronized (LOCK) {
560: mutating = false;
561: if (result != null) {
562: setModel(result);
563: }
564: }
565: }
566: if (fresh) {
567: fireChange(new TwoWayEvent.Recreated<DM, UMD, DMD>(this ,
568: oldValue, result, derivedDelta));
569: } else {
570: fireChange(new TwoWayEvent.Clobbered<DM, UMD, DMD>(this ,
571: oldValue, result, derivedDelta));
572: }
573: return result;
574: }
575:
576: /**
577: * Indicate that any current value of the derived model is invalid and
578: * should no longer be used if exact results are desired.
579: * <p>This method requires the read lock but does not block otherwise,
580: * except to call {@link #composeUnderlyingDeltas}.
581: * @param underlyingDelta a change to the underlying model
582: */
583: public final void invalidate(UMD underlyingDelta) {
584: if (underlyingDelta == null)
585: throw new NullPointerException();
586: assert lock.canRead();
587: boolean wasInited;
588: DM oldValue;
589: synchronized (LOCK) {
590: assertStateConsistent();
591: assert !mutating;
592: if (this .underlyingDelta != null) {
593: // XXX don't call this with LOCK held
594: // may then need to have an 'invalidating' flag (?)
595: this .underlyingDelta = composeUnderlyingDeltas(
596: this .underlyingDelta, underlyingDelta);
597: } else {
598: this .underlyingDelta = underlyingDelta;
599: }
600: wasInited = fresh || problem != null;
601: if (fresh) {
602: fresh = false;
603: }
604: oldValue = (model != null) ? model.get() : null;
605: problem = null;
606: }
607: if (wasInited && oldValue != null) {
608: fireChange(new TwoWayEvent.Invalidated<DM, UMD, DMD>(this ,
609: oldValue, underlyingDelta));
610: }
611: }
612:
613: /**
614: * Initiate creation of the derived model from the underlying model.
615: * This is a no-op unless that process has not yet been started or if the
616: * value of the derived model is already fresh and needs no rederivation.
617: * <p>This method does not require the lock nor does it block, except
618: * insofar as {@link #initiating} might.
619: */
620: public final void initiate() {
621: boolean isInitiating = false;
622: synchronized (LOCK) {
623: assertStateConsistent();
624: if (!active && !fresh) {
625: DM oldValue = (model != null) ? model.get() : null;
626: DeriveTask<DM, UMD, DMD> t = new DeriveTask<DM, UMD, DMD>(
627: this , oldValue != null || problem != null);
628: toDerive.add(t);
629: tasks.put(this , t);
630: active = true;
631: startDerivationThread();
632: isInitiating = true;
633: LOCK.notifyAll();
634: }
635: }
636: if (isInitiating) {
637: initiating();
638: }
639: }
640:
641: /**
642: * Called during {@link #initiate}.
643: * The default implementation does nothing. Subclasses may choose to initiate
644: * a request for some information from the underlying model, if it is not
645: * immediately accessible.
646: * <p>This method is not called with any lock, so if a read lock is desired,
647: * it must be requested explicitly.
648: */
649: protected void initiating() {
650: // do nothing
651: }
652:
653: /**
654: * Add a listener to lifecycle changes in the support.
655: * <p>A listener may be added multiple times and must be removed once
656: * for each add.
657: * <p>This method may be called from any thread and will not block.
658: * @param l a listener to add
659: */
660: public final void addTwoWayListener(TwoWayListener<DM, UMD, DMD> l) {
661: synchronized (listeners) {
662: listeners.add(l);
663: }
664: }
665:
666: /**
667: * Add a listener to lifecycle changes in the support.
668: * <p>This method may be called from any thread and will not block.
669: * @param l a listener to remove
670: */
671: public final void removeTwoWayListener(
672: TwoWayListener<DM, UMD, DMD> l) {
673: synchronized (listeners) {
674: listeners.remove(l);
675: }
676: }
677:
678: /**
679: * Fire an event to all listeners in the read lock.
680: */
681: private void fireChange(final TwoWayEvent<DM, UMD, DMD> e) {
682: final List<TwoWayListener<DM, UMD, DMD>> ls;
683: synchronized (listeners) {
684: if (listeners.isEmpty()) {
685: return;
686: }
687: ls = new ArrayList<TwoWayListener<DM, UMD, DMD>>(listeners);
688: }
689: lock.read(new Runnable() {
690: public void run() {
691: for (TwoWayListener<DM, UMD, DMD> l : ls) {
692: if (e instanceof TwoWayEvent.Derived) {
693: l
694: .derived((TwoWayEvent.Derived<DM, UMD, DMD>) e);
695: } else if (e instanceof TwoWayEvent.Invalidated) {
696: l
697: .invalidated((TwoWayEvent.Invalidated<DM, UMD, DMD>) e);
698: } else if (e instanceof TwoWayEvent.Recreated) {
699: l
700: .recreated((TwoWayEvent.Recreated<DM, UMD, DMD>) e);
701: } else if (e instanceof TwoWayEvent.Clobbered) {
702: l
703: .clobbered((TwoWayEvent.Clobbered<DM, UMD, DMD>) e);
704: } else if (e instanceof TwoWayEvent.Forgotten) {
705: l
706: .forgotten((TwoWayEvent.Forgotten<DM, UMD, DMD>) e);
707: } else {
708: assert e instanceof TwoWayEvent.Broken;
709: l.broken((TwoWayEvent.Broken<DM, UMD, DMD>) e);
710: }
711: }
712: }
713: });
714: }
715:
716: /**
717: * Supply an optional delay before rederivation of a model after an invalidation.
718: * If zero (the default), there is no intentional delay. The delay is irrelevant
719: * in the case of {@link #getValueBlocking}.
720: * @return a delay in milliseconds (>= 0)
721: */
722: protected long delay() {
723: return 0L;
724: }
725:
726: /**
727: * Indicate whether this support permits changes to the derived model via
728: * {@link #mutate} to "clobber" underived changes to the underlying model.
729: * If false (the default), such attempts will throw {@link ClobberException}.
730: * If true, they will be permitted, though a clobber event will be notified
731: * rather than a recreate event.
732: * <p>A subclass must always return the same value from this method.
733: * @return true to permit clobbering, false to forbid it
734: */
735: protected boolean permitsClobbering() {
736: return false;
737: }
738:
739: private Reference<DM> createEnqueuedReference(DM value) {
740: Reference<DM> r = createReference(value, queue);
741: if (!(r instanceof StrongReference) && queue == null) {
742: // Well discard that one; optimistically assumed that
743: // createReference is not overridden, in which case we
744: // never actually have to make a queue.
745: queue = new ReferenceQueue();
746: r = createReference(value, queue);
747: Thread t = new Thread(new QueuePollingThread(),
748: "TwoWaySupport.QueuePollingThread");
749: t.setPriority(Thread.MIN_PRIORITY);
750: t.setDaemon(true);
751: t.start();
752: }
753: return r;
754: }
755:
756: private static final class QueuePollingThread implements Runnable {
757:
758: public void run() {
759: while (true) {
760: try {
761: Reference r = queue.remove();
762: TwoWaySupport s;
763: synchronized (LOCK) {
764: Reference<TwoWaySupport> r2 = referencesToSupports
765: .remove(r);
766: s = (r2 != null) ? r2.get() : null;
767: }
768: if (s != null) {
769: notify(s);
770: }
771: } catch (InterruptedException e) {
772: assert false : e;
773: }
774: }
775: }
776:
777: @SuppressWarnings("unchecked")
778: private void notify(TwoWaySupport s) {
779: s.fireChange(new TwoWayEvent.Forgotten(s));
780: }
781:
782: }
783:
784: /**
785: * Create a reference to the derived model.
786: * The support will only retain this reference (though event objects will
787: * strongly refer to the derived model when appropriate).
788: * If the referent is collected, the support returns to an underived state.
789: *
790: * <p>This implementation always creates a strong reference that will never
791: * be collected so long as the support itself is not collected.
792: * @param value a derived model object
793: * @param q a reference queue supplied by the support
794: * @return a reference to the model enqueued on that reference queue
795: */
796: protected Reference<DM> createReference(DM value, ReferenceQueue q) {
797: // Does not matter what the queue is.
798: return new StrongReference<DM>(value);
799: }
800:
801: /**
802: * A strong reference whose referent will not be collected unless the
803: * reference is too.
804: */
805: private static final class StrongReference<DM> extends
806: WeakReference<DM> {
807: private DM value;
808:
809: public StrongReference(DM value) {
810: super (value);
811: assert value != null;
812: this .value = value;
813: }
814:
815: public DM get() {
816: return value;
817: }
818:
819: public void clear() {
820: super .clear();
821: value = null;
822: }
823: }
824:
825: static final class DeriveTask<DM, UMD, DMD> implements
826: Comparable<DeriveTask<DM, UMD, DMD>> {
827:
828: public final Reference<TwoWaySupport<DM, UMD, DMD>> support;
829:
830: public final long schedule;
831:
832: public DeriveTask(TwoWaySupport<DM, UMD, DMD> support,
833: boolean delay) {
834: this .support = new WeakReference<TwoWaySupport<DM, UMD, DMD>>(
835: support);
836: schedule = System.currentTimeMillis()
837: + (delay ? support.delay() : 0L);
838: }
839:
840: public int compareTo(DeriveTask<DM, UMD, DMD> t) {
841: if (t == this )
842: return 0;
843: if (schedule > t.schedule)
844: return 1;
845: if (schedule < t.schedule)
846: return -1;
847: return hashCode() - t.hashCode();
848: }
849:
850: }
851:
852: private static void startDerivationThread() {
853: synchronized (LOCK) {
854: if (!startedThread) {
855: Thread t = new Thread(new DerivationThread(),
856: "TwoWaySupport.DerivationThread");
857: t.setPriority(Thread.MIN_PRIORITY);
858: t.setDaemon(true);
859: t.start();
860: startedThread = true;
861: }
862: }
863: }
864:
865: private static final class DerivationThread implements Runnable {
866:
867: public void run() {
868: while (true) {
869: // Javac thinks it "might already have been assigned" by the time it is
870: // actually assigned below.
871: final TwoWaySupport[] s = new TwoWaySupport[1];
872: synchronized (LOCK) {
873: while (toDerive.isEmpty()) {
874: logger.finer("derivation thread waiting...");
875: try {
876: LOCK.wait();
877: } catch (InterruptedException e) {
878: assert false : e;
879: }
880: }
881: Iterator<DeriveTask> it = toDerive.iterator();
882: DeriveTask t = it.next();
883: s[0] = (TwoWaySupport) t.support.get();
884: logger.log(Level.FINER,
885: "derivation thread found: {0}", s[0]);
886: if (s[0] == null) {
887: // Dead - support was collected before we got to it.
888: it.remove();
889: continue;
890: }
891: long now = System.currentTimeMillis();
892: if (t.schedule > now) {
893: logger
894: .log(
895: Level.FINER,
896: "derivation thread deferring: {0} for {1}msec",
897: new Object[] { s[0],
898: t.schedule - now });
899: try {
900: LOCK.wait(t.schedule - now);
901: } catch (InterruptedException e) {
902: assert false : e;
903: }
904: // Try again in next round.
905: continue;
906: }
907: }
908: logger.log(Level.FINER,
909: "derivation thread processing: {0}", s[0]);
910: // Out of synch block; we have a support to run.
911: s[0].getLock().read(new Runnable() {
912: public void run() {
913: try {
914: s[0].getValueBlocking();
915: // Ignore value and exceptions - gVB is
916: // enough to cache that info and fire changes.
917: } catch (InvocationTargetException e) {
918: // OK, handled separately.
919: } catch (RuntimeException e) {
920: e.printStackTrace();
921: } catch (Error e) {
922: e.printStackTrace();
923: }
924: }
925: });
926: // Don't explicitly remove it from the queue - if it was
927: // active, then gVB should have done that itself.
928: }
929: }
930:
931: }
932:
933: }
|