Quick and Simple Caching With Apache Commons JCS

Need a quick and easy caching system for your web app? Don’t you dare roll your own. And don’t even think about doing some kind of quick and dirty HashMap. Setting up a simple cache with Commons JCS™, is easy – you can have a basic LRU Cache up and running in minutes. What’s more, the package is mature and sophisticated, so you can get into more advanced features when the need arises. But let’s take a look at a simple setup.

First, the Maven dependency…

<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-jcs-core</artifactId> <version>2.0-beta-1</version> </dependency>

Next, create the cache config file as follows. Name it cache.ccf and put it in your resources directory on the classpath. The first stanza is the default cache. The second is one I created, for example, to cache a list of people (active users) from an expensive API call into the cloud.

# DEFAULT CACHE REGION jcs.default=DC jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes jcs.default.cacheattributes.MaxObjects=1000 jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache jcs.default.cacheattributes.UseMemoryShrinker=false jcs.default.cacheattributes.MaxMemoryIdleTime=3600 jcs.default.cacheattributes.ShrinkerInterval=60 jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes jcs.default.elementattributes.IsEternal=false jcs.default.elementattributes.MaxLife=21600 jcs.default.elementattributes.IdleTime=1800 jcs.default.elementattributes.IsSpool=true jcs.default.elementattributes.IsRemote=true jcs.default.elementattributes.IsLateral=true # PRE-DEFINED CACHE REGIONS jcs.region.peopleCache=DC jcs.region.peopleCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes jcs.region.peopleCache.cacheattributes.MaxObjects=1000 jcs.region.peopleCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache jcs.region.peopleCache.cacheattributes.UseMemoryShrinker=false jcs.region.peopleCache.cacheattributes.MaxMemoryIdleTime=3600 jcs.region.peopleCache.cacheattributes.ShrinkerInterval=60 jcs.region.peopleCache.cacheattributes.MaxSpoolPerRun=500 jcs.region.peopleCache.elementattributes=org.apache.commons.jcs.engine.ElementAttributes jcs.region.peopleCache.elementattributes.IsEternal=false

You’ll need the following imports…

import org.apache.commons.jcs.JCS; import org.apache.commons.jcs.access.exception.CacheException; import org.apache.commons.jcs.access.CacheAccess;

Get a handle on the cache by name like this…

// String is the cache key, the second param is whatever object type you're caching... private CacheAccess<String, List<User>> cache = null; public MyService() { try { cache = JCS.getInstance( "peopleCache" ); } catch ( CacheException e ) { LOG.error( String.format( "Problem initializing cache: %s", e.getMessage() ) ); } }

Finally, here’s an example of a fetch method that attempts to get resources from the cache before going back to the expensive API client request.

public List getActiveUsers() { // First, try to get the user list from the cache... List userList = cache.get("activeUserList"); // If the userList does not exist in the cache, build it // from the repository request and stick it in the cache. if(userList == null) { UserCollection users = client.getUsers(); userList = new ArrayList(); for (User user : users) { if(user.isActive()) { userList.add(user); } } cache.put("activeUserList",userList); } return userList; }

That’s all you need to do to setup a cache in your web app fast. But if you’re wondering if Commons JCS™ has the chops to handle something more sophisticated, here’s something to ease your concerns. Beyond simply caching objects in memory, it provides numerous additional features:

  • Memory management
  • Disk overflow (and defragmentation)
  • Thread pool controls
  • Element grouping
  • Minimal dependencies
  • Quick nested categorical removal
  • Data expiration (idle time and max life)
  • Extensible framework
  • Fully configurable runtime parameters
  • Region data separation and configuration
  • Fine grained element configuration options
  • Remote synchronization
  • Remote store recovery
  • Non-blocking “zombie” (balking facade) pattern
  • Lateral distribution of elements via HTTP, TCP, or UDP
  • UDP Discovery of other caches
  • Element event handling
  • Remote server chaining (or clustering) and failover
  • Custom event logging hooks
  • Custom event queue injection
  • Custom object serializer injection
  • Key pattern matching retrieval
  • Network efficient multi-key retrieval

Before integrating Commons JCS™, be sure to check out possible cache features available on whatever platform you’re building for. For example, you might instead use the built-in DynaCache for IBM WebSphere or the IBM Data Cache for Bluemix. If nothing is already available on your platform, Commons JCS™ can be the way to go.