Friday, 2 September 2016

Introduction to Java Caching System

Introduction 


In most of the web applications, data is retrieved from the database. The database operation is expensive and time consuming. Present-day web applications are data intensive and first response time is the basic criteria for success. If the web application is frequently accessing the database for each request then its performance will be slow. As a result, many web applications are following different design techniques for reducing latency times and scale up.

JCS is a composite distributed caching system that works on JDK 1.4 and higher versions. The performance of JCS is very impressive for low put and high read applications.

The following are some of the major features of Java Caching System:
  • Memory management
  • Thread pool control
  • Extensible framework
  • Configurable runtime parameters
  • Key based data retrieval
  • Distributed composite cache
  • Element event handling

JCS – The Composite Cache


The foundation of Java Caching System is based on composite cache. There are four types of caches available for each region. Any type of cache can be plugged into the JCS configuration to make it active. The following are the four different types of caches:
  • Memory Cache (LRU): This is the widely used basic caching mechanism. The memory cache is extremely fast. It uses Least Recently Used (LRU) algorithm to manage the objects stored in the memory. The LRU memory cache is based on its own LRU Map implementation. This Map implementation is much faster compared to LRUMap implementation or any other type of Map.
  • Disk Cache (Indexed): The disk cache is used to store data when the memory cache is full. When indexing is used with the disk cache, the performance is much faster. Queue based process is used to store data on the disk. The disk cache is completely configurable. A thread pool is used to manage the queue worker threads that also make the process faster.
  • JDBC Disk Cache: This is another type of disk cache which is reliable, fast and fully configurable. The basic storage is JDBC compatible database. Both the keys and values are stored in the database. The data is stored as a BLOB and the expired data elements are removed periodically. Thread pool can also be used to manage queue worker threads for faster processing.
  • TCP Lateral Cache: This is a mechanism to distribute cached data on multiple distributed servers. This is also a completely configurable caching system. It can also use the UDP discovery mechanism to add nodes without disturbing the entire system. The TCP Lateral Cache establishes connections with socket servers on other nodes and performs the storage management. One server is sufficient to manage multiple regions.
  • Remote Cache using RMI: This is another caching option provided by JCS. In this mechanism, each node is not required to connect with other nodes. The remote cache server can be used as a connection point and each node can connect with it. The remote cache server is used to hold a serialized version of your object. The remote server can be grouped with other servers to recover failover.

Dependency Configuration


In the following section we will discuss a simple application using JCS. Before moving into it, we must have the following dependencies in the CLASSPATH. Please download the executable jars and put them in the CLASSPATH to build the application.
  • jcs-1.3.jar
  • commons-logging.jar
  • commons-lang-2.4.jar
  • concurrent.jar

Working with JCS


In this section we will discuss different components of Java Caching System and the configuration details. We will explain each component with a working example.

The following is a simple Employee class holding employee details. The class should implement Serializable interface as its data will be persisted. It contains getter/setter methods for reading and writing data.

Listing 1: Showing Employee class


public class Employee implements java.io.Serializable{
private String name;
private String address;
private String empid;
public Employee(String name, String address, String empid) {
   this.name = name;
   this.address = address;
   this.empid = empid;
    }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getEmpid() {
return empid;
}
public void setEmpid(String empid) {
this.empid = empid;
}
}

The following is the main EmployeeManager class which imports JCS packages to use the framework APIs. It uses a cache region known as 'empCache' to put and get data into/from memory. The data is stored in the cache memory by using a key/value pair. The retrieval is based on the key associated with each value. The application is showing addition/retrieval and removal of objects stored in a cache.

Listing 2: Showing EmployeeManager class


import org.apache.jcs.JCS;
import org.apache.jcs.access.exception.CacheException;
public class EmployeeManager {
private JCS cache;
public EmployeeManager()
 {
   try
   {
     // Load the cache
     cache = JCS.getInstance( "empCache" );
     // Initialize the cache, Here data can be loaded during initialization
     cache.put( "123", new Employee( "Nick", "Detroit.USA", "123" ) );
     cache.put( "143", new Employee( "Ric",  "Seattle.USA", "143" ) );
     cache.put( "153", new Employee( "Jhon", "Chicago.USA", "153" ) );
     cache.put( "163", new Employee( "Dan", "Houston.USA", "163" ) );
   }
   catch( CacheException e )
   {
     e.printStackTrace();
   }
 }
public void addEmployee( Employee emp )
 {
   try
   {
     cache.put( emp.getEmpid(), emp );
   }
   catch( CacheException e )
   {
     e.printStackTrace();
   }
 }
 public Employee getEmployee( String empid )
 {
   return ( Employee )cache.get( empid );
 }
 public void removeEmployee( String empid )
 {
   try
   {
     cache.remove( empid );
   }
   catch( CacheException e )
   {
     e.printStackTrace();
   }
 }
 public static void main( String[] args )
 {
   // Create the employee manager
   EmployeeManager empManager = new EmployeeManager();
   // Add employees to the employee manager
   /*empManager.addEmployee(new Employee("Name1", "address1", "empid1"));
   empManager.addEmployee(new Employee("Name2", "address2", "empid2"));
   empManager.addEmployee(new Employee("Name3", "address3", "empid3"));*/
   // Get employee
   Employee emp = empManager.getEmployee("123");
   System.out.println( "Employee details retrieved from cache: " + emp.getName()+"-"+emp.getAddress());
   // Remove employee
   empManager.removeEmployee("123");
   // After removal of employee
   System.out.println( "Employee details after removal from cache: " + empManager.getEmployee("123") );
 }
}

The following is the configuration file cache.ccf. It holds all the configurable parameters with their values. The first section is the default cache region which holds the default values. If no custom region is configured then these default values will be used. The second section is defining the custom cache region which is empCache here. It defines the custom parameter values. The last section is the AUXILIARY CACHE region that describes the disk cache and other related parameters.

Listing 3: Showing configuration file


# DEFAULT CACHE REGION
jcs.default=DC
jcs.default.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes
jcs.default.cacheattributes.MaxObjects=1000
jcs.default.cacheattributes.MemoryCacheName=org.apache.jcs.engine.memory.lru.LRUMemoryCache
jcs.default.cacheattributes.UseMemoryShrinker=false
jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
jcs.default.elementattributes=org.apache.jcs.engine.ElementAttributes
jcs.default.elementattributes.IsEternal=false
jcs.default.elementattributes.MaxLifeSeconds=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.empCache=DC
jcs.region.empCache.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes
jcs.region.empCache.cacheattributes.MaxObjects=1000
jcs.region.empCache.cacheattributes.MemoryCacheName=org.apache.jcs.engine.memory.lru.LRUMemoryCache
jcs.region.empCache.cacheattributes.UseMemoryShrinker=false
jcs.region.empCache.cacheattributes.MaxMemoryIdleTimeSeconds=3600
jcs.region.empCache.cacheattributes.ShrinkerIntervalSeconds=60
jcs.region.empCache.cacheattributes.MaxSpoolPerRun=500
jcs.region.empCache.elementattributes=org.apache.jcs.engine.ElementAttributes
jcs.region.empCache.elementattributes.IsEternal=false
# AVAILABLE AUXILIARY CACHES
jcs.auxiliary.DC=org.apache.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
jcs.auxiliary.DC.attributes=org.apache.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
jcs.auxiliary.DC.attributes.DiskPath=c:/temp
jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000000
jcs.auxiliary.DC.attributes.MaxKeySize=1000000
jcs.auxiliary.DC.attributes.MaxRecycleBinSize=5000
jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
jcs.auxiliary.DC.attributes.ShutdownSpoolTimeLimit=60