Source Code Cross Referenced for TwoWaySupport.java in  » IDE-Netbeans » performance » threaddemo » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » performance » threaddemo.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.