F.2 The IceUtil Package
The Cache class allows you to efficiently maintain a cache that is backed by secondary storage, such as a Berkeley DB database, without holding a lock on the entire cache while values are being loaded from the database. If you want to create evictors (see
Section 32.9.4) for servants that store their state in a database, the
Cache class can simplify your evictor implementation considerably.
1
The Cache class has the following interface:
package IceUtil;
public class Cache {
public Cache(Store store);
public Object pin(Object key);
public Object pin(Object key, Object o);
public Object unpin(Object key);
public Object putIfAbsent(Object key, Object newObj);
public Object getIfPinned(Object key);
public void clear();
public int size();
}
Internally, a Cache maintains a map of name–value pairs. The implementation of
Cache takes care of maintaining the map; in particular, it ensures that concurrent lookups by callers are possible without blocking even if some of the callers are currently loading values from the backing store. In turn, this is useful for evictor implementations, such as the Freeze
BackgroundSaveEvictor. The
Cache class does not limit the number of entries in the cache—it is the job of the evictor implementation to limit the map size by calling
unpin on elements of the map that it wants to evict.
The Cache class works in conjunction with a
Store interface for which you must provide an implementation. The
Store interface is trivial:
package IceUtil;
public interface Store {
Object load(Object key);
}
You must implement the load method in a class that you derive from
Store. The
Cache implementation calls
load when it needs to retrieve the value for the passed key from the backing store. If
load cannot locate a record for the given key because no such record exists, it must return null. If
load fails for some other reason, it can throw an exception derived from
java.lang.RuntimeException, which is propagated back to the application code.
To add a key–value pair to the cache, your evictor can call
pin. The return value is null if the key and value were added; otherwise, if the map already contains an entry with the given key, the entry is unchanged and
pin returns the original value for that key.
This version of pin does
not call
load to retrieve the entry from backing store if it is not yet in the cache. This is useful when you add a newly-created object to the cache.
This version of pin returns the value stored in the cache for the given key if the cache already contains an entry for that key. If no entry with the given key is in the cache,
pin calls
load to retrieve the corresponding value (if any) from the backing store.
pin returns the value returned by
load, that is, the value if
load could retrieve it, null if
load could not retrieve it, or any exception thrown by
load.
unpin removes the entry for the given key from the cache. If the cache contained an entry for the key, the return value is the value for that key; otherwise, the return value is null.
This function adds a key–value pair to the cache. If the cache already contains an entry for the given key,
putIfAbsent returns the original value for that key. If no entry with the given key is in the cache,
putIfAbsent calls
load to retrieve the corresponding entry (if any) from the backing store and returns the value returned by
load.