I shelved this work for a few weeks, but got back on it last night.
To start with, I merged the StatefulInstanceManager and
SessionSynchronizationCoordinator into the Stateful container. In my
opinion, this vastly simplifies the code and because all bean call
backs and the general EJB lifecycle is much more visible (and
readable). Then I extracted a simple cache interface from the
container:
public interface Cache<K, V> {
/**
* Add a new entry to the cache. The entry is marked checked-out
and can
* not be accessed again until checked-in.
*
* @IllegalStateException if an value is already associated with
the key
*/
void add(K key, V value);
/**
* Marks the entry checked-out, so this entry can not be accessed
until
* checked-in.
*
* @throws IllegalStateException if the entry is already checked
out.
* @throws Exception if an entry is loaded and the afterLoad
method threw an
* exception
*/
V checkOut(K key) throws Exception;
/**
* Marks the entry available, so it can be accessed again.
*
* @throws IllegalStateException if the entry is not checked out.
*/
void checkIn(K key);
/**
* Removes the entry from the cache.
*/
V remove(K key);
/**
* Removes all of th entries that match the specified filter.
*/
void removeAll(CacheFilter<V> filter);
/**
* Callback listener for cache events.
*/
public interface CacheListener<V> {
/**
* Called after an entry is loaded from a store.
*
* @throws Exception if there is a problem with the instance
*/
void afterLoad(V value) throws Exception;
/**
* Called before an entry is written to a store.
*
* @throws Exception if there is a problem with the instance
*/
void beforeStore(V value) throws Exception;
/**
* Called when an instance has been removed from the cache
due to a
* time-out.
*/
void timedOut(V value);
}
/**
* CacheFileter is used to select values to remove during a
removeAll
* invocation.
*/
public interface CacheFilter<V> {
/**
* True if the filter matches the value.
*/
boolean matches(V v);
}
}
Generally, the container checks out an instance when a method is
invoked, and checks it back in when the transaction completes. This
behavior is due to the Stateful specification that an instance can not
be passivated (e.g., written to disk, distributed across a cluster,
etc.) while in a transaction. The container is notified when the
cache loads or stores an instance so it can make the proper
ejbActivate/ejbPassivate calls.
I believe this interface is simple enough that we can adapt it to a
great verity of cache implementations, but we won't know until we
write some :)
-dain
On Jul 2, 2008, at 3:39 PM, Dain Sundstrom wrote:
Hi all,
I've been thinking about SFSB caching, and have a few ideas about
cache implements I'd like to experiment with like using Java5
concurrent collections, ehCache, ServletSession and maybe
Terracotta. Before I start experimenting, I'm going to try to
simplify the interface between the StatefulContainer and
StatefulInstanceManager (the cache). Right now, if you want to
plugin in a new cache, you have to either write a lot of EJB
specific logic or subclass from our the existing
StatefulInstanceManager, which sucks. My goal is to extract a
simple interface with just a few Map like operations that one would
need to implement to introduce a new cache.
Anyway, just a heads up. I'll post again when I have a initial
draft of the interface available. I'll also try to keep the
existing extensions of StatefulInstanceManager in Geronimo working.
-dain teracotta