Chapter 2. Introduction

2.1. Overview

JBoss Cache consists of two components, Core Cache, and POJO Cache. Core Cache provides efficient memory storage, transactions, replication, eviction, persistent storage, and many other "core" features you would expect from a distributed cache. The Core Cache API is tree based. Data is arranged on the tree using nodes that each offer a map of attributes. This map-like API is intuitive and easy to use for caching data, but just like the Java Collection API, it operates only off of simple and serializable types. Therefore, it has the following constraints:

  • If replication or persistence is needed, the object will then need to implement the Serializable interface. E.g.,
    public Class Foo implements Serializable
  • If the object is mutable, any field change will require a successive put operation on the cache:
    value = new Foo();
    cache.put(fqn, key, value);
    value.update(); // update value
    cache.put(fqn, key, value); // Need to repeat this step again to ask cache to persist or replicate the changes
  • Java serialization always writes the entire object, even if only one field was changed. Therefore, large objects can have significant overhead, especially if they are updated frequently:
    thousand = new ThousandFieldObject();
    cache.put(fqn, key, thousand);
    thousand.setField1("blah"); // Only one field was modified 
    cache.put(fqn, key, thousand); // Replicates 1000 fields
  • The object structure can not have a graph relationship. That is, the object can not have references to objects that are shared (multiple referenced) or to itself (cyclic). Otherwise, the relationship will be broken upon serialization (e.g., when replicate each parent object separately). For example, Figure 1 illustrates this problem during replication. If we have two Person instances that share the same Address , upon replication, it will be split into two separate Address instances (instead of just one). The following is the code snippet using Cache that illustrates this problem:
    joe = new Person("joe");
    mary = new Person("mary");
    addr = new Address("Taipei");
    joe.setAddress(addr);
    mary.setAddress(addr);
    cache.put("/joe", "person", joe);
    cache.put("/mary", "person", mary);
    

Illustration of shared objects problem during replication

Figure 2.1. Illustration of shared objects problem during replication

POJO Cache attempts to address these issues by building a layer on top of Core Cache which transparently maps normal Java object model operations to individual Node operations on the cache. This offers the following improvements:

  • Objects do not need to implement Serializable interface. Instead they are instrumented, allowing POJO Cache to intercept individual operations.
  • Replication is fine-grained. Only modified fields are replicated, and they can be optionally batched in a transaction.
  • Object identity is preserved, so graphs and cyclical references are allowed.
  • Once attached to the cache, all subsequent object operationis will trigger a cache operation (like replication) automatically:
    POJO pojo = new POJO();
    pojoCache.attach("id", pojo);
    pojo.setName("some pojo"); // This will trigger replication automatically.
    

In POJO Cache, these are the typical development and programming steps:

  • Annotate your object with @Replicable
  • Use attach() to put your POJO under cache management.
  • Operate on the object directly. The cache will then manage the replication or persistence automatically and transparently.

More details on these steps will be given in later chapters.

Since POJO Cache is a layer on-top of Core Cache, all features available in Core Cache are also available in POJO Cache. Furthermore, you can obtain an instance to the underlying Core Cache by calling PojoCache.getCache(). This is useful for resusing the same cache instance to store custom data, along with the POJO model.

2.2. Features

Here are the current features and benefits of PojoCache:

  • Fine-grained replication. The replication modes supported are the same as that of Core Cache: LOCAL, REPL_SYNC, REPL_ASYNC, INVALIDATION_SYNC, and INVALIDATION_ASYNC (see the main JBoss Cache reference documentation for details). The replication level is fine-grained and is performed automatically once the POJO is mapped into the internal cache store. When a POJO field is updated, a replication request will be sent out only to the key corresponding to that modified attribute (instead of the whole object). This can have a potential performance boost during the replication process; e.g., updating a single key in a big HashMap will only replicate the single field instead of the whole map!

  • Transactions. All attached objects participate in a user transaction context. If a rollback occurs, the previous internal field state of the object will be restored:

    POJO p = new POJO();
    p.setName("old value");
    pojoCache.attach("id", p);
    tx.begin(); // start a user transaction
    p.setName("some pojo");
    tx.rollback(); // this will cause the rollback
    p.getName(); // is "old value"
    

    In addition, operations under a transaction is batched. That is, the update is not performed until the commit phase. Further, if replication is enabled, other nodes will not see the changes until the transaction has completed successfully.

  • Passivation. POJO Cache supports the same passivation provided by Core Cache. When a node mapped by POJO Cache has reached a configured threshold, it is evicted from memory and stored using a cache loader. When the node is accessed again, it will be retrieved from the cache loader and put into memory. The configuration parameters are the same as those of the Cache counterpart. To configure the passivation, you will need to configure both the eviction policy and cache loader.

  • Object cache by reachability, i.e., recursive object mapping into the cache store. On attach, POJO Cache will attach all referenced objects as well. This feature is explained in more detail later.

  • Natural Object Relationships. Java references are preserved as they were written. That is, a user does not need to declare any object relationship (e.g., one-to-one, or one-to-many) to use the cache.

  • Object Identity. Object identity is preserved. Not only can a cached object be compared using equals(), but the comparison operator, ==, can be used as well. For example, an object such as Address may be multiple referenced by two Persons (e.g., joe and mary). The objects retrieved from joe.getAddress() and mary.getAddress() should be identicali, when when retrieved from a different node in the cluster then that which attached them.

  • Inheritance. POJO Cache preserves the inheritance hierarchy of any object in the cache. For example, if a Student class inherits from a Person class, once a Student object is mapped to POJO Cache (e.g., attach call), the fields in the base class Person are mapped as well.

  • Collections. Java Collection types (e.g. List, Set, and Map) are transparently mapped using Java proxies. Details are described later.

  • Annotation based. Starting from release 2.0, JDK 5 annotations are used to indicate that an object should be instrumented for use under POJO Cache (once attached).

  • Transparent. Once a POJO is attached to the cache, subsequent object model changes are transparently handled. No further API calls are required.

2.3. Usage

To use POJO Cache, you obtain the instance from the PojoCacheFactory by supplying a config file that is used by the delegating Cache implementation. Once the PojoCache instance is obtained, you can call the cache life cycle method to start the cache. Below is a code snippet that creates and starts the cache:

String configFile = "replSync-service.xml";
boolean toStart = false;
PojoCache pcache = PojoCacheFactory.createCache(configFiel, toStart);
pcache.start(); // if toStart above is true, it will starts the cache automatically.
pcache.attach(id, pojo);
...
pcache.stop(); // stop the cache. This will take PojoCache out of the clustering group, if any, e.g.

2.4. Requirements

POJO Cache is currently supported on JDK 5 (since release 2.0). It requires the following libraries (in addition to jboss-cache.jar and the required libraries for Core Cache) to start up:

  • Library:

    • pojocache.jar. Main POJO Cache library.
    • jboss-aop-jdk50.jar. Main JBoss Aop library.
    • javassist.jar. Java byte code manipulation library.
    • trove.jar. High performance collections for Java.