Monday, March 06, 2006

Java Object Caching with Java Caching System

Java Caching System is a distributed caching system written in Java. In this post I discuss the basic overview of how to use JCS and point to the relevant parts of the documentation of the product for further information. Most of the information has been gathered and organized from the site. The foundation of JCS is the composite cache.Four types of caches can be plugged into the Composite Cache for any given region:
  • LRU Memory Cache: An extremely fast, highly configurable memory cache. It uses the Least recently used algorithm to manage the number of items that can be stored in the cache.
  • Indexed disk cache: Fast, reliable and highly configurable swap for cached data. Cache elements are written to disk via a continuous queue-based process. Every aspect fo the disk cache is configurable, and a thread pool can be used to reduce the number of queue worker threads across the system.
  • TCP Lateral cache: Provides an easy way to distribute cache data into multiple servers. Uses a UDP discovery mechanism so that the entire farm need not be reconfigured when a new node is added. Each node maintains a connection to every other. TCP lateral can be configured to change most interactions each node has with it's peers.
  • RMI Remote cache: A remote cache server can be configured as a central connection point for all the nodes instead of each maintaining a connection with the every other node. The remote server broadcasts events (updates, invalidations etc.) generated on one node to the others. The remote cache server holds a serialized version of your objects, so it does not need to be deployed with your class libraries.
Writing code for the Java Caching system is quite simple.

import org.apache.jcs.JCS;
import org.apache.jcs.access.exception.CacheException;
. . .
private static final String cacheRegionName = "city";
private JCS cache = null;
. . .
// in your constructor you might do this
try {
setCache( JCS.getInstance( this.getCacheRegionName() ) );
} catch ( CacheException e ) {
log.error( "Problem initializing cache for region name ["
+ this.getCacheRegionName() + "].", e );
}
. . .
// to get a city out of the cache by id you might do this:
String key = "cityId:" + String.valueOf( id );
City city = (City) cache.get( key );
. . .
// to put a city object in the cache, you could do this:
try {
// if it isn't null, insert it
if ( city != null ) {
cache.put( key, city );
}
} catch ( CacheException e ) {
log.error( "Problem putting "
+ city + " in the cache, for key " + key, e );
}
The following is a small description of how the requirements that I outlined in the caching requirements post can be implemented using JCS.
  • Dependencies: As of version 1.2.7.0, the core of JCS (the LRU memory cache, the indexed disk cache, the TCP lateral, and the RMI remote server) requires only two other jars.
    • concurrent
    • commons-logging
    Versions 1.2.6.9 and below also require the following two additional jars:
    • commons-collections
    • commons-lang
  • Element grouping:The JCS provides feature rich grouping mechanism, where groups of elements can be invalidated and whose attributes can be listed.
  • Data expiration: Data expiration can be controlled at the individual element level. This can be achieved declaratively as well as programmatically. In order to declare the expiration charecteristics within a region, add the following under the region definition.

    jcs.default.elementattributes.IsEternal=false
    jcs.default.elementattributes.MaxLifeSeconds=700
    jcs.default.elementattributes.IdleTime=1800
    jcs.default.elementattributes.IsSpool=true
    jcs.default.elementattributes.IsRemote=true

    This applies to all elements within a region and if declared in the defaults, will apply to elements in all regions.
    In order to programmatically set the expiration properties, then you can do so using IElementAttributes as shown below:
    // jcs.getDefaultElementAttributes returns a copy not a reference
    IElementAttributes attributes = jcs.getDefaultElementAttributes();
    // set some special value
    attributes.setIsEternal( true );
    jcs.setDefaultElementAttributes( attributes );

  • Configurable runtime parameters: Runtime parameters can be configured in the cache.ccf file. The parameter that can be configured at the element level are discussed in the element sections. The configuration properties that can be set at the region level are discusses here. The properties that can be configured at the region level include maximun objects, cache name, size, shrink interval etc. An example is shown below

    jcs.default.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes
    jcs.default.cacheattributes.MaxObjects=200001
    jcs.default.cacheattributes.MemoryCacheName=
    org.apache.jcs.engine.memory.lru.LRUMemoryCache
    jcs.default.cacheattributes.UseMemoryShrinker=true
    jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
    jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
    jcs.default.elementattributes=
    org.apache.jcs.engine.ElementAttributes
  • Disk overflow (and defragmentation)
    Thread pool controls
    Region data separation and configuration

    More information on configuring auxilaries can be found on the site as they have extensive configurable properties.
  • Element event handling: JCS allows to attach event handlers to elements in the local memory cache (does not work for auxilaries, i.e. lateral, remote and disk caches). To create an event handler you must implement the org.apache.jcs.engine.control.event.behavior.IElementEventHandler interface. This interface contains only one method:
    public void handleElementEvent( IElementEvent event );

    The IElementEvent object contains both the event code and the source. The source is the element for which the event occurred. Once you have an ElementEventHandler implementation, you can attach it to an element via the Element Attributes as shown below
    MyEventHandler meh = new MyEventHandler();
    // jcs.getDefaultElementAttributes returns a copy not a reference
    IElementAttributes attributes = jcs.getDefaultElementAttributes();
    attributes.addElementEventHandler( meh );
    jcs.put( "key", "data", attributes );
  • Fine grained element configuration options: As discussed above.
  • Non-blocking "zombie" (balking facade) pattern:
    Lateral distribution of elements via HTTP, TCP, or UDP
    UDP Discovery of other caches

    JCS uses the zombie pattern in the Lateral TCP Cache auxilary. More information may be found here.
  • Remote synchronization
    Remote store recovery
    Remote server chaining (or clustering) and failover
    Information about the configuration options for clustering can be found at Remote auxilary caching

3 comments:

  1. Please provide a usefull example. The details you posted is available on apache website. Simply copying and pasting in the blog is not a good idea and not usefull to the users. Please put your own conrcrete findings. Thanks

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. That might help https://github.com/igaidai4uk/jcs-test

    ReplyDelete