org.hd.d.pg2k.webSvr.exhibit
Class AbstractFilterBean

java.lang.Object
  extended by org.hd.d.pg2k.webSvr.exhibit.AbstractFilterBean
All Implemented Interfaces:
java.io.Serializable
Direct Known Subclasses:
FilterBean, TreeFilterBean

public abstract class AbstractFilterBean
extends java.lang.Object
implements java.io.Serializable

JavaBean that can filter/sort and cache selections of exhibits. Designed to be used as a bean in a JSP or servlet, and can cache filter/sort results, for example, at application level or in a user's session.

This applies its filter to the input List (or entire exhibit set) that it is provided with; deriving classes may make all or part of the result directly or indirectly available.

This silently recomputes its results if they may have been affected by any update in the exhibit data (or when select() is called differently from the last call to the same instance). The cached state is not persisted if the bean is serialised, but the filter description is. This is only meant to be persisted, for example, to be passed between multiple instances of a distributable WAR and not to be stored for lengthy periods on disc, so does not use some of the fancier support for serialisation.

This uses but does not cache a DataSourceBean which can be supplied explicitly or recovered implicitly from a servlet context (at application scope), and can take the whole set of exhibits as input or can use the output of another filter. In all cases the output is only recomputed if the DataSourceBean.exhibitDataVersionHash() method result changes (so any randomised or conditional element had better be the last stage of the chain and only have (say) page or request scope), or if select() is called with different args to the last call on this instance (so, if for example, a different List argument is passed; select does a test on the hash code of the select() List arg).

This is thread-safe and as concurrent as possible, with the intention that it may be involved in many users' activity simultaneously.

The expression must be set (ideally exactly once) before any attempt is made to fetch results. For JSPs this set can be done in the body of the JSP so that it is done only on creation of the bean as a small efficiency gain.

If the expression is changed and is not equals() to the previous value, the cache is cleared.

The result returned by any select() operation is an immutable, thread-safe, RandomAccess List (eg like ArrayList). (This cannot be enforced on derived classes, but is part of the general contract.)

The expression is of the form:

     expr = expr sortExpr
          | expr filterExpr
          | ALL
     sortExpr   = sort(args)
     filterExpr = filter(args)
                | filterExpr filterExpr &&
                | filterExpr filterExpr ||
                | filterExpr !
 
ie a filter on its own is applied to all the available exhibits, or can be negated or combined (ANDed or ORed) with other filter expressions to make more complex filter expressions. A complete expression is a series of filter expressions and/or sorts applied in a pipeline to the result of some filtering.

The expression syntax is reverse Polish amd dataflow is from left to right.

This class implements the Observer interface and when it receives a notification its immediately clears its cache by default. This is in addition to the normal automatic cache discard in select() when the imput has changed, and is an optimisation; behvaiour is correct without it. This mechanism helps discard stale data quickly and thus make better use of memory.

This can also be set to expire automatically from time to time, for example to allow for slowly mutating/changing data such as ExhibitPropsComputableMutable items. The default is not to expire periodically.

This Serializable so as to be able to be stored in a servlet session; nothing especially long-lived or sensitive.

TODO: Should do validation on deserialisation.

See Also:
Serialized Form

Nested Class Summary
private static class AbstractFilterBean.ExpiryTask
          Task to actively expire a filter and release its memory.
private static class AbstractFilterBean.MyObserver
          This object has only a WeakReference to the outer class to allow GC.
private static class AbstractFilterBean.WeakenerTask
          Task to weaken retained references to soft from strong.
 
Field Summary
private  java.lang.Object _basicCache
          The cached results (or null if not yet computed).
private  java.lang.Object _retainedCacheRefs
          Collection of all references to internal cache objects requested to be retained; null when none present.
private  long _select_runTime
          Records the time taken to complete _select() and descendants; non-negative and 0 if not yet called (or if deserialised).
private  long _select_startTime
          Records the time at which _select() was last started; 0 if not yet called (or if deserialised).
private  java.util.TimerTask _ttExpiry
          Timer task if any, to eagerly force expiry and release of resources, associated with this cache.
private  java.util.TimerTask _ttPurgeRetainedRefs
          A task for clearing the _retainedCacheRefs; ephemeral and usually null.
protected static int BUILD_TIME_LIVE_RATIO
          Minimum multiple of build time for which we attempt to keep a memory-sensitive cache live; strictly positive.
protected static int BUILD_TIME_LIVE_RATIO_MEM_STRESS
          Minimum multiple of build time for which we attempt to keep a memory-sensitive cache live when memory may be short; strictly positive.
private  java.lang.Runnable emergencyFreeHook
          Callback for 'emergency free' from MemoryTools; never null after registration.
private  int exhibitDataHash
          The exhibit data hash when the cache was computed.
private  int expiryInterval
          Expiry interval in ms, zero (the default) means no automatic expiry; non-negative.
private  long expiryTime
          Expiry time of the cache (ie its content is to be considered invalid after this).
private  Expr expr
          The (filter/sorter) expression, or null if not yet set.
private  int lastListArgHashCode
          The hashCode of the last List arg to select or 0 if none.
private static java.util.Timer lifetimeManager
          Timer used to help manage the cache lifetimes for memory-sensitive caches.
protected static int MAX_STRONG_RETENTION_MS
          Maximum strong-ref retention time in ms; strictly positive.
private  boolean memorySensitiveCache
          If true then led cached value go if we run short of memory, eg hold it via a SoftReference.
private  java.lang.String name
          Name for this instance; by default null.
private static java.lang.ref.SoftReference<java.util.Collection<java.lang.Object>> NO_REFS
          Fixed dead SoftReference to use to clear _retainedCacheRefs without allocating.
private static boolean OBSERVE_DATASOURCEBEAN
          If true, become an Observer of the DataSourceBean so that we can eagerly discard a stale cache.
private  AbstractFilterBean.MyObserver observer
          Embedded observer object.
private static long serialVersionUID
          Unique Serialisation class ID generated by http://random.hd.org/.
 
Constructor Summary
AbstractFilterBean()
           
 
Method Summary
private  void _clearRetainedRefs()
          Clears retained references without taking any locks.
private static int _computeListArgHashCode(java.util.List<Name.ExhibitFull> inputSet)
          Compute the hash over our List argument, used by _isValid() and _select().
protected  java.util.List<Name.ExhibitFull> _isValid(DataSourceBean ds, java.util.List<Name.ExhibitFull> inputSet)
          Returns cache reference if the cache still seems to be valid.
protected  void _retainCacheRef(java.lang.Object newRef)
          Add to the retained refs an cached internal object that must be maintained to avoid the cache expiring.
protected  java.util.List<Name.ExhibitFull> _select(DataSourceBean ds, java.util.List<Name.ExhibitFull> inputSet)
          Internal method that computes the result of accepted exhibits.
protected  void clearCache()
          Clears the cache of the basic set of filtered/sorted items.
private  int getExhibitDataHash()
          Get exhibitDataHash.
 int getExpiryInterval()
          Get expiry interval in ms, zero (the default) means no automatic expiry; non-negative.
protected  Expr getExpr()
          Get the simple single (input) filter/sort expression directly.
 boolean getMemorySensitiveCache()
          Get the `memory-sensitive' flag for the cache.
 java.lang.String getName()
          Get the name for this instance; by default null.
 void invalidate()
          Invalidate the cache.
private  void setExhibitDataHash(int exhibitDataHash)
          Get exhibitDataHash.
 void setExpiryInterval(int expiryInterval)
          Set expiry interval in ms, zero means no automatic expiry; non-negative.
 void setExpr(Expr _expr)
          Set the simple single (input) filter/sort expression directly.
 void setExpr(java.lang.String _expr)
          Set the filter/sorter expression as a textual expression.
 void setMemorySensitiveCache(boolean f)
          Set the `memory-sensitive' flag for the cache.
 void setName(java.lang.String n)
          Set the name for this instance; by default null.
 java.lang.String toString()
          Get human-readable summary; never null.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

OBSERVE_DATASOURCEBEAN

private static final boolean OBSERVE_DATASOURCEBEAN
If true, become an Observer of the DataSourceBean so that we can eagerly discard a stale cache. Observing the DataSourceBean should not cause a deadlock as we do so only indirectly/asynchronously.

See Also:
Constant Field Values

expr

private Expr expr
The (filter/sorter) expression, or null if not yet set. Accessed under the instance lock.

This is persisted if the object is serialised.


memorySensitiveCache

private boolean memorySensitiveCache
If true then led cached value go if we run short of memory, eg hold it via a SoftReference. This is useful for infrequently-accessed and not-too-expensive filters.

This can be set or reset at any time but is only effective when the cache is filled, so is probably done once, before the first set of values is retrieved.

By default this value is true and the cache is memory sensitive, ie won't cause the system to run out of memory.

Accessed under the instance lock.

This is persisted if the object is serialised.


expiryInterval

private int expiryInterval
Expiry interval in ms, zero (the default) means no automatic expiry; non-negative. The data may expire in a little less than this.

Is volatile so as to allow access without a lock.


exhibitDataHash

private transient volatile int exhibitDataHash
The exhibit data hash when the cache was computed. If this changes we should regard the cache as invalid.

Is volatile so that it can be accessed from multiple threads without taking a lock.

Should be accessed only by getExhibitDataHash() and setExhibitDataHash().

Zero when the cache is clear/invalid (the initial state).


lastListArgHashCode

private transient int lastListArgHashCode
The hashCode of the last List arg to select or 0 if none. This is used to try to cheaply ensure that we discard our cache if our calling pattern changes (which should not happen, but this may save us from poor usage).

Accessed under the instance lock and is private to _select() and clearCache().

Zero when the cache is clear.


_basicCache

private transient java.lang.Object _basicCache
The cached results (or null if not yet computed). This consists of an immutable RandomAccess List, not sorted unless the expression sorts it, of the accepted results; no slots in the List are duplicates nor nulls.

If the exhibit data hash (or select()-arg hash) changes then we should regard the cache as invalid.

This is null only if not yet computed; it is zero length if the result set was empty.

Accessed under the instance lock and is private to _select() and clearCache().

Is either a List or a SoftReference wrapper around one.


observer

private final AbstractFilterBean.MyObserver observer
Embedded observer object. Designed to break link between DataSourceBean and our class to allow us to be GCed even if we are recorded as an observer.

This inner object has only a SoftReference to us.


emergencyFreeHook

private java.lang.Runnable emergencyFreeHook
Callback for 'emergency free' from MemoryTools; never null after registration. We create and register this when we first call _select() and re-register it when it is called back.

When it is called it unguards any SoftReferences allowing memory to be freed rather than an OutOfMemoryException getting thrown somewhere in the application.

It is assumed that MemoryTools holds this via a WeakReference so that it will not inhibit this instance being GCed.


name

private java.lang.String name
Name for this instance; by default null.


_ttExpiry

private transient java.util.TimerTask _ttExpiry
Timer task if any, to eagerly force expiry and release of resources, associated with this cache. Marked transient since we couldn't persist this but in any case after deserialisation would expect it to be set up again if needed.

Accessed under the instance lock.


expiryTime

private transient long expiryTime
Expiry time of the cache (ie its content is to be considered invalid after this). Private to _isValid and accessed in the scope of its lock (instance lock). May also be peeked at by the ExpiryTask under the lock.

Transient to force an immediate recomputation if deserialised.


_select_startTime

private transient long _select_startTime
Records the time at which _select() was last started; 0 if not yet called (or if deserialised). This effectively (conservatively) notes when the cached value was last requested/used, as well as providing a baseline to measure how long value (re)generation took.

Only written to by _select(), under the instance lock.


_select_runTime

private transient long _select_runTime
Records the time taken to complete _select() and descendants; non-negative and 0 if not yet called (or if deserialised). This is based on when the last request was made to hold a strong reference to internal state.

May not be maintained if the cache is not memory sensitive.

TODO: May be used to give the cached state a new lease of life on each access.

Only written to by _retainCacheRef(), under the instance lock.


serialVersionUID

private static final long serialVersionUID
Unique Serialisation class ID generated by http://random.hd.org/.

See Also:
Constant Field Values

_retainedCacheRefs

private transient volatile java.lang.Object _retainedCacheRefs
Collection of all references to internal cache objects requested to be retained; null when none present. This is used to prevent otherwise-soft cache references being cleared too soon.

Intended primarily for memory-sensitive caches though will do no harm will occur if references to internal cached state are always posted here.

Erased/dropped entirely when the cache is invalidated.

Cleared or switched to being themselves held via a soft reference whenever the initial lifetime is over, but not until _select() and its descendants have finished their computation. May then be switched back to being a strong reference for a while on each read access of not already expired, to provide another memory-insensitive period.

Created in _select() when a new value is computed. Each derived class should post its recomputed cached values (under the instance lock) after (re)computing them, to prevent them expiring too soon.

Accessed under the instance lock except marked volatile so that it may safely be cleared without a lock by _clearRetainedRefs().


NO_REFS

private static final java.lang.ref.SoftReference<java.util.Collection<java.lang.Object>> NO_REFS
Fixed dead SoftReference to use to clear _retainedCacheRefs without allocating. May be null during class initialisation.


_ttPurgeRetainedRefs

private transient java.util.TimerTask _ttPurgeRetainedRefs
A task for clearing the _retainedCacheRefs; ephemeral and usually null. Cleared when computing a new value in _select() and descendants, also when the cache is invalidated, and when this task itself completes, and killed and recreated as each new item is added to _retainedCacheRefs to reflect the time taken to compute the values so far on this run.

Helps retain the cache for a minimum lifetime.


BUILD_TIME_LIVE_RATIO

protected static final int BUILD_TIME_LIVE_RATIO
Minimum multiple of build time for which we attempt to keep a memory-sensitive cache live; strictly positive. Increasing this value reduces the fraction of wall-clock time that we may have to spend rebuilding, but increases the risk of causing memory starvation elsewhere.

TODO: may replace with dynamic calculation of ratio large enough to keep amortised build time to <~100ms per _select().

A reasonable value is probably in the range 5--1000; a power of two may prove marginally more efficient.

See Also:
Constant Field Values

BUILD_TIME_LIVE_RATIO_MEM_STRESS

protected static final int BUILD_TIME_LIVE_RATIO_MEM_STRESS
Minimum multiple of build time for which we attempt to keep a memory-sensitive cache live when memory may be short; strictly positive. Should be (much) smaller than BUILD_TIME_LIVE_RATIO.

A reasonable value is probably in the range 2--32; a power of two may prove marginally more efficient.

See Also:
Constant Field Values

MAX_STRONG_RETENTION_MS

protected static final int MAX_STRONG_RETENTION_MS
Maximum strong-ref retention time in ms; strictly positive. This helps bound the length of the task queues, etc.

Should be a very long time in computing terms, eg minutes through to hours.


lifetimeManager

private static final java.util.Timer lifetimeManager
Timer used to help manage the cache lifetimes for memory-sensitive caches. Is a daemon to avoid keeping the JVM alive.

Should never be cancelled (will be GCed in due course if this class is).

It is very important that any tasks not due to run immediately should only hold onto AFB references weakly (and thus must be static classed for example) to avoid retaining garbage and compounding the problem that is intended to solve!

Constructor Detail

AbstractFilterBean

public AbstractFilterBean()
Method Detail

getExpiryInterval

public int getExpiryInterval()
Get expiry interval in ms, zero (the default) means no automatic expiry; non-negative. The data may expire in a little less than this.


setExpiryInterval

public void setExpiryInterval(int expiryInterval)
Set expiry interval in ms, zero means no automatic expiry; non-negative. The data may expire in a little less than this at random to spread out expiries and recalculations, or may expire more slowly if the system is short of power or otherwise under stress to help reduce system load.


getExhibitDataHash

private int getExhibitDataHash()
Get exhibitDataHash.


setExhibitDataHash

private void setExhibitDataHash(int exhibitDataHash)
Get exhibitDataHash.


getName

public java.lang.String getName()
Get the name for this instance; by default null.


setName

public void setName(java.lang.String n)
Set the name for this instance; by default null.


toString

public java.lang.String toString()
Get human-readable summary; never null.

Overrides:
toString in class java.lang.Object

setMemorySensitiveCache

public void setMemorySensitiveCache(boolean f)
Set the `memory-sensitive' flag for the cache. This is useful for infrequently-accessed and not-too-expensive filters; if set true then the cache is automatically discarded if we run short of heap space.

This can be set or reset at any time but is only effective when the cache is filled, so is probably best done once, before the first set of values is retrieved.

By default this value is true and the cache is not memory sensitive.

This is persisted if the object is serialised.


getMemorySensitiveCache

public boolean getMemorySensitiveCache()
Get the `memory-sensitive' flag for the cache.


setExpr

public void setExpr(Expr _expr)
             throws java.lang.IllegalArgumentException,
                    java.lang.IllegalStateException
Set the simple single (input) filter/sort expression directly. If the expression is set to a new value not equals() to the old, then the cache is cleared.

Throws:
java.lang.IllegalArgumentException - if the argument is null
java.lang.IllegalStateException

getExpr

protected Expr getExpr()
Get the simple single (input) filter/sort expression directly. Has protected scope to prevent every Tom Dick and Harry fiddling with a possibly-mutable expression.

This can be overridden to provide some extra filtering or sorting, for example, in derived classes.

Returns:
the expression or null if none yet set

setExpr

public void setExpr(java.lang.String _expr)
             throws java.lang.IllegalArgumentException,
                    java.lang.IllegalStateException
Set the filter/sorter expression as a textual expression. Will clear the cache if the expression changes.

Throws:
java.lang.IllegalArgumentException - if the argument is null or unparseable
java.lang.IllegalStateException

clearCache

protected void clearCache()
Clears the cache of the basic set of filtered/sorted items. Replaces any cached item with null and resets the internal hashes of the data and argument to null.

A deriving class can override this to clear any extra state it maintains, but must chain to this first in order to clear the underlying caches and free other resources.

This routine is synchronized and overriding methods almost certainly should be so too.


invalidate

public void invalidate()
Invalidate the cache. Can be called by anyone; may force the cache to be invalidated immediately or before next use.

May need to grab locks.


_isValid

protected java.util.List<Name.ExhibitFull> _isValid(DataSourceBean ds,
                                                    java.util.List<Name.ExhibitFull> inputSet)
                                             throws java.io.IOException
Returns cache reference if the cache still seems to be valid. If null, then the cache needs to be rebuilt. As a side-effect, this will clear an invalid cache.

Parameters as for _select(), return value as for _select() or null if invalid.

Cheaper than calling _select() to force the cache to be invalidated since it does not attempt to rebuild the cache.

Throws:
java.io.IOException

_computeListArgHashCode

private static int _computeListArgHashCode(java.util.List<Name.ExhibitFull> inputSet)
Compute the hash over our List argument, used by _isValid() and _select(). Note that the identityHashCode() is used.


_select

protected java.util.List<Name.ExhibitFull> _select(DataSourceBean ds,
                                                   java.util.List<Name.ExhibitFull> inputSet)
                                            throws java.io.IOException,
                                                   java.lang.IllegalStateException
Internal method that computes the result of accepted exhibits. The result is an immutable list of full exhibit names.

The ds argument must not be null.

If the inputSet argument is null the whole set of exhibits is used, else the supplied set is used. This must be a List of String items with no nulls nor duplicates.

This will (re)compute its results if necessary, ie:

otherwise this routine is relatively fast and returns the cached List.

This never returns null.

Null names and names that do not represent items currently in the exhibit set are silently dropped.

This routine may register this instance with the data source as an Observer of exhibit set changes in order to be able to quickly invalidate/release our (then-stale) cache upon any changes.

Parameters:
inputSet - null, or a list of full exhibit names
Throws:
java.lang.IllegalStateException - if the filter has not been set
java.io.IOException

_clearRetainedRefs

private void _clearRetainedRefs()
Clears retained references without taking any locks. Suitable for use in an emergency, eg when memory is very low.


_retainCacheRef

protected void _retainCacheRef(java.lang.Object newRef)
Add to the retained refs an cached internal object that must be maintained to avoid the cache expiring. This should only be called by descendants/callers of _select() and under the same instance lock.

Automatically creates/replaces the timer task to clear this.

We go through the motions even if not currently a memory-sensitive cache since that status may be changed at any time.

Parameters:
newRef - reference to retain, or null to revive existing references if possible

DHD Multimedia Gallery V1.57.21

Copyright (c) 1996-2011, Damon Hart-Davis. All rights reserved.