Caching is an oft overlooked element of an application. It can speed up applications dramatically.
This library consists of two different types of caches. The first part are simple type of caches like a static cache or a last recent updated cache. The other part is the TreeCache which makes managing dependencies between objects simple.
The interface for all caches is very straight forward. Here are all methods:
public void put(Object identifier,Object value);
Stores an object with the given identifier in the cache
public Object get(Object identifier);
Returns the object with the given identifier from the cache if it is available
public int getSize();
Returns the current size (number of stored objects)
public int getCapacity();
Returns the total capacity (maximum number of objects)
public void delete(Object identifier);
Removes the object with the given identifier
public void clear();
Deletes all objects
public String getDomainName();
Returns the domain name for this cache
public List getAllElements();
Returns all objects
public boolean contains(Object identifier);
Returns true if an object with this identifier is in the cache
public Set keySet();
Returns all identifiers as Set
The CacheManager takes care of managing all caches. It keeps a map of all caches referenced by their unique domain name. This manager is implemented as a Singleton so that all classes in the same JVM can have access to a cache. The cache will be instantiated once and then is stored internally by the CacheManager. In order to get a cache call the getCache method. There are three different getCache methods available:
getCache(String domainName,int type)
This returns a cache for the given type with the specified domain name. The capacity is set to 2000 objects and the Time-to-Live ("ttl") is set to -1. This means that the objects in the cache will not expire.
getCache(String domainName,int type,int capacity)
Like the above but defines the capacity.
getCache(String domainName,int type,int capacity,long ttl)
Sets all values. LRU = 1; TTL = 2; STATIC = 3; FILE_CONTENT = 4; TRANSFORMER = 6;
3.1. The list of supported types
The jZonic-cache supports the following types:
Table 1.
LRU (Least-Recently-Used) |
1 |
TTL (Time-to-Live) |
2 |
STATIC |
3 |
FILE_CONTENT |
4 |
TRANSFORMER |
6 |
The number five was ignored for technical reasons. The different types are explain in a later chapter.
4. The different types of caches
This chapter explains the different types that the jZonic-cache offers.
The static cache will store the objects until a the maximum number of objects is reached. This maximum is defined by the capacity. The default value is 2000 objects.
The LRU cache will store objects in the cache until the maximum number of objects is reached. Every new object is moved to the top of the internal list. Each time when an object is requested it is moved to the top. If the maximum number of objects is reached, the new object will be stored at the first place in the list and the last one is removed.
This is a timer based cache. Every object has a certain lifetime and will be removed automatically if the object expires. There is also a maximum number of objects defined.
This cache will store the text content of files. It extends the LRU cache. The full qualified filename must be used as qualifier. If the file has changed then the cache will reload it automatically the next time the entry will be accessed.
This cache is meant for storing the XSL transformer. This is a helpful cache when it comes to XML/XSL processing. Using a cached transformer will speed up the step of transforming dramatically.
The CacheHandler can be used to load and store the content of a cache.
5.1. SerializeCacheHandler
This handler will serialize the cache to a file and read it back. All objects in the cache must be serializable otherwise the content cannot be stored.
Parameter:
filename: the name of the file that the cache will be stored
5.2. PeriodSerializeCacheHandler
This handler is the same as the one above except that it will store the content periodically to the file. You do not have to call "storeCache" since this is done automatically.
Parameter:
filename: the name of the file that the cache will be stored
period (optional): the interval whenever the cache will be stored in seconds. The default value is 30 minutes.
All types of caches supports a listener that notifies all registered listeners if an entry in the cache has been updated or deleted.
In order to register as a listener your class has to implement the CacheListener interface. Here is an example:
import org.jzonic.core.cache.events.*;
public class MyListenerTest implements CacheListener {
public void cacheCleared() {
}
public void cacheUpdated(CacheEvent ce) {
}
}
This is a simple example that will demonstrate the listener:
import org.jzonic.core.cache.events.*;
import org.jzonic.core.cache.*;
public class ListenerExample implements CacheListener {
public ListenerExample() {
}
public void cacheCleared() {
System.out.println("the cache was cleared");
}
public void cacheUpdated(CacheEvent ce) {
if ( ce.getType() == CacheEvent.ENTRY_DELETED ) {
System.out.println("the entry:"+ce.getIdentifier()+" has been deleted");
}
if ( ce.getType() == CacheEvent.ENTRY_UPDATED ) {
System.out.println("the entry:"+ce.getIdentifier()+" has been updated");
}
}
public static void main(String[] args) {
Cache myCache = CacheManager.getCache("MyCache",CacheManager.STATIC);
myCache.addCacheListener(new ListenerExample());
myCache.put("Hello","World");
myCache.delete("Hello");
myCache.put("And","More to come");
myCache.clear();
}
}
This will write out:
the entry:Hello has been updated
the entry:Hello has been deleted
the entry:And has been updated
the cache was cleared
7. Configuring caches with the cache.xml
In order to prepare certain caches you can use the cache.xml to define them. Make sure that this file is in the classpath of your application.
The jZonic-Cache does not support the use of a property file to configure certain caches. An XML file is easier to understand and more intuitive to use than a property file. The XML structure is fairly simple:
<?xml version="1.0"?>
<cache-settings>
<cache name="{name of the cache}">
<type>{static|lru|ttl|filecontent|transformer}</type>
<capacity>{maximum size}</capacity>
<ttl>{seconds to live}</ttl>
<handler class="{full qualified name of the handler}">
<parameter name="{parameter name}" value="{parameter value}"/>
</handler>
</cache>
</cache-settings>
Inside the "cache-settings" tags you can define as many caches as you need. The "cache" tag requires the name attribute which is the domain name of the cache. The "type" tag defines which type of cache you want and the capacity the maximum number of elements. TTL tag defines the time to live in seconds if the cache supports this.
The "handler" tag is optional. It requires the "class" attribute which must be the full qualified name of the handler and additional sub tags as parameters which will be send to the handler.
This is a simple example that will define one static cache called "MyCache". This cache has a capacity of 200 objects that can be stored.
<?xml version="1.0"?>
<cache-settings>
<cache name="MyCache">
<type>static</type>
<capacity>200</capacity>
</cache>
</cache-settings>
The second example will prepare two caches. One static cache called "MyStatic" with a handler and the second one is a TTL cache where every object will expire after 30 minutes:
<?xml version="1.0"?>
<cache-settings>
<cache name="MyStatic">
<type>static</type>
<capacity>1500</capacity>
<handler class="org.jzonic.core.cache.handler.PeriodSerializeCacheHandler">
<parameter name="file" value="/tmp/mydir/myfile"/>
<parameter name="period" value="60"/>
</handler>
</cache>
<cache name="MyTTL">
<type>TTL</type>
<capacity>1000</capacity>
<ttl>1800</ttl>
</cache>
</cache-settings>
So far all cache types used a linear approach for storing the objects. Every object must be associated with an unique key. Sometimes it is helpful to overcome this shortcoming and build a tree of cache objects. This lets you easily manage dependencies between objects. For example you can store the user information in the cache and all his related objects as children. So when you remove the user from the cache all of his objects will be deleted as well.
The TreeCache uses node names that are build like "root/groupa/groupb". Every node has one parent and can have multiple children. When you insert an object into the cache the node will be created automatically. The cache will assume that the parent name is the same without the last part. If this parent does not exist it will be created until the node name is a root element.
The TreeCache does not implement the cache interface since the method signatures are different to the regular cache. You can create nodes in the tree and add objects to this node. The nodes are created automatically when you add an object to a node. In order to get an object from the cache you need to specify the path to the node and the key for the object as child of the node. Therefore here is a list of methods that the TreeCache supports
public static TreeCache getInstance(String domainName)
This method returns a TreeCache for the given domain name. If the cache does not exist so far it will be created.
public void clear()
This method will remove all elements from the cache.
public void delete(String identifier)
This method deletes a node and all of its children from the tree.
public void delete(String identifier,String name)
This method deletes an object with the given name from a node with the specific identifier
public Object get(String identifier,String nodeName)
The method returns an object from the cache. The cache will search for a node with the given name "identifier" and the key "nodeName"
public boolean contains(String identifier)
Use this method to determine if there is a node with the given name
public int getSize()
This returns the current size
public String getDomainName()
Returns the domain name of the cache
public void put(String nodeName,String key,Object value)
Puts an object into the cache at the given node name for the given key. If the parent node does not exist it will be created.
Now we will look at some simple examples that should help you getting started.
import org.jzonic.core.cache.*;
public class TreeCacheDemo {
public static void main(String[] args) {
// get an instance of our cache
TreeCache tc = TreeCache.getInstance("Test");
// insert a few items
tc.put("root","item1","Hello");
tc.put("root","item2","World");
tc.put("root/groupa/subgroupa","More","Stuff");
// we have 3 nodes inside
// root, groupa and subgroupa
System.out.println("We have "+tc.getSize()+" nodes");
String value = (String)tc.get("root/groupa/subgroupa","More");
// this will write out Stuff
System.out.println("Value is:"+value);
}
}
Next is an example where we will delete an object
import org.jzonic.core.cache.*;
public class TreeCacheDemo2 {
public static void main(String[] args) {
// get an instance of our cache
TreeCache tc = TreeCache.getInstance("Test");
// insert a few items
tc.put("root","item1","Hello");
tc.put("root/groupa/subgroupa","More","Stuff");
// now we delete root/groupa
tc.delete("root/groupa");
// and the root/groupa/subgroupa is also gone
String value = (String)tc.get("root/groupa/subgroupa","More");
// this will write out null
System.out.println("Value is:"+value);
}
}
The jZonic-cache uses jLo as its logging framework. This library is in the lib directory. When you want to use the cache make sure you have the jlo.jar in your classpath.
The logging framework will search for the log configuration which is stored in a file called jlo_logging.xml. This file is also included in the distribution. You have to make sure that the file is also in the classpath. Otherwise the logger will write everything to the console.
For more informations about jLo please refer to the website http://jlo.jzonic.org.
The is a list of changes
- javadoc improved
- CacheEvent and CacheListener added
- TreeCacheListener and TreeCacheEvent added
- CacheHandler interface and SerializeCacheHandler, PeriodSerializeCacheHandler added
- cache configuration added (simple XML configuration) - several bug fixes in the TTL cache