Hi folks. I recently joined the jdbm developers. In order
to get familiar with the codebase, I've been working on
adding a cache based on soft references. While doing this,
I ran into some areas of jdbm.helper.CachePolicy that
were not defined in (the excrutiating) enough detail for
After chatting with Alex, here are the clarifications
that are going to be introduced. Also below is the design
for the soft cache. I plan to commit these changes and
addition to the CVS tip tommorrow. Please let me know if
doing so would cause unbearable suffering.
1. Attempting to add a null listener will result in
IllegalArgumentException being thrown.
2. Attempts to add a listener whose object.equal() already
exists in the list will be ignored (the first listener
stays.) This is different from the current MRU: it adds
duplicate references to lists.
3. Since only one instance of equal listeners is added to
a cache, the first remove( ) will prevent further
notification on that listener. This is different from
the current MRU: it removes the first occurance only.
Since it allows duplicate listings, remove(listener)
does not guarantee that listener is nolonger attached
to the cache in question.
CachePolicy object evictions
4. If a listener throws a CacheEvictionException while
handling a cacheObjectEvicted event, then the cache
propagates the exception. The cache itself remains
unchanged: (a) the object being evicted stays in
the cache, and (b) the new key-object pair that was
being added is not added. The current MRU satisfies
condition #b, but not #a.
Given current pure Java APIs, it is not possible to build
a CachePolicy implementation based on soft references.
This is because the CachePolicy requires listeners be
given the *actual object* being evicted, rather than the
key of the object being evicted. By the time users of
java.lang.ref.SoftReference find out that the object is
being evicted, it is too late; the referent is nolonger
accessible. It is not possible to use a PhantomReference
as a backdoor, either: it's get() method is disabled.
There are two ways to circumvent this limitation. One is
to internally maintain clones of all the objects in the
cache. Besides bending the existing contract, this is
fool-hardy because it effectively halves the amount of
objects the cache can store.
The second way is to pre-emptively fire eviction events.
This, in essence, is wrapping another CachePolicy with
a Level-II (L2) cache.
I've opted for the second. Rather than re-articulate
the semantics of the component, I've attached the
javadoc page for the class. Of particular interest
is the documentation for the class and the get()
All thoughts and criticisms are welcome.