eXo Platform 3.5

## Chapter 62. eXo Cache

This chapter will provide you all the basic knowledge about eXo Cache, from basic concepts to advanced concepts, sample codes, and more.

## Basic concepts

All applications on the top of eXo JCR that need a cache, can rely on an `org.exoplatform.services.cache.ExoCache` instance that is managed by the `org.exoplatform.services.cache.CacheService`. The main implementation of this service is `org.exoplatform.services.cache.impl.CacheServiceImpl` which depends on the `org.exoplatform.services.cache.ExoCacheConfig` in order to create new `ExoCache` instances. See the below example of `org.exoplatform.services.cache.CacheService` definition:

      <component>

        <key>org.exoplatform.services.cache.CacheService</key>

        <jmx-name>cache:type=CacheService</jmx-name>

        <type>org.exoplatform.services.cache.impl.CacheServiceImpl</type>

        <init-params>

          <object-param>

            <name>cache.config.default</name>

            <description>The default cache configuration</description>

            <object type="org.exoplatform.services.cache.ExoCacheConfig">

              <field name="name"><string>default</string></field>

              <field name="maxSize"><int>300</int></field>

              <field name="liveTime"><long>600</long></field>

              <field name="distributed"><boolean>false</boolean></field>

              <field name="implementation"><string>org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache</string></field>

            </object>

          </object-param>

        </init-params>

      </component>

### Note

The `ExoCacheConfig` which name is `default`, will be the default configuration of all the `ExoCache` instances that don't have dedicated configuration.

See the below example about how to define a new `ExoCacheConfig` thanks to a external-component-plugin:

      <external-component-plugins>

        <target-component>org.exoplatform.services.cache.CacheService</target-component>

        <component-plugin>

          <name>addExoCacheConfig</name>

          <set-method>addExoCacheConfig</set-method>

          <type>org.exoplatform.services.cache.ExoCacheConfigPlugin</type>

          <description>Configures the cache for query service</description>

          <init-params>

            <object-param>

              <name>cache.config.wcm.composer</name>

              <description>The default cache configuration</description>

              <object type="org.exoplatform.services.cache.ExoCacheConfig">

                <field name="name"><string>wcm.composer</string></field>

                <field name="maxSize"><int>300</int></field>

                <field name="liveTime"><long>600</long></field>

                <field name="distributed"><boolean>false</boolean></field>

                <field name="implementation"><string>org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache</string></field>

              </object>

            </object-param>

          </init-params>

        </component-plugin>

      </external-component-plugins>

name | The name of the cache. This field is mandatory since it will be used to retrieve the `ExoCacheConfig` corresponding to a given cache name.
label | The label of the cache. This field is optional. It is mainly used to indicate the purpose of the cache.
maxSize | The maximum numbers of elements in cache. This field is mandatory.
liveTime | The amount of time (in seconds) that an element is not written or read before it is evicted. This field is mandatory.
implementation | The full qualified name of the cache implementation to use. This field is optional. This field is only used for simple cache implementation. The default and main implementation is `org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache`, this implementation only works with local caches with FIFO as eviction policy. For more complex implementation see the next sections.
distributed | Indicates if the cache is distributed. This field is optional. This field is deprecated.
replicated | Indicates if the cache is replicated. This field is optional.
logEnabled | Indicates if the log is enabled. This field is optional. This field is used for backward compatibility.
avoidValueReplication | Indicates whether the values of the cache should be replicated or not in case of a replicated cache. This field is optional. By default it is disabled. Find more details about this field in the next section.

## Advanced concepts

### Invalidation

In case, you have big values or non serializable values and you need a replicated cache to at list invalidate the data when it is needed, you can use the invalidation mode that will work on top of any replicated cache implementations. This is possible thanks to the class InvalidationExoCache which is actually a decorator whose idea is to replicate the the hash code of the value in order to know if it is needed or not to invalidate the local data, if the new hash code of the value is the same as the old value, we assume that it is the same value so we don't invalidate the old value. This is required to avoid the following infinite loop that we will face with invalidation mode proposed out of the box by JBoss Cache for example:

1. Cluster node #1 puts (key1, value1) into the cache

2. On cluster node #2 key1 is invalidated by put call in node #1

3. Node #2 re-loads key1 and puts (key1, value1) into the cache

4. On cluster node #1 key1 is invalidated, so we get back to step #1

In the use case above, thanks to the InvalidationExoCache since the value loaded at step #3 has the same hash code as the value loaded as step #1, the step #4 won't invalidate the data on the cluster node #1.

It exists 2 ways to use the invalidation mode which are the following:

1. By configuration: For this you simply need to set the parameter avoidValueReplication to true in your eXo cache configuration, this will indicate the CacheService to wrap your eXo cache instance into an InvalidationExoCache in case the cache is defined as replicated or distributed.

2. Programmatically; You can wrap your eXo cache instance into an org.exoplatform.services.cache.invalidation.InvalidationExoCache yourself using the public constructors that are available. Please note that if you use CacheListeners add them to the InvalidationExoCache instance instead of the nested eXo Cache because the nested eXo Cache will contain only hash codes so the related listeners will get hash codes instead of the real values.

### Note

The invalidation will be efficient if and only if the hash code method is properly implemented, in other words 2 value objects representing the same data need to return the same hash code otherwise the infinite loop described above will still be effective.

### FutureExoCache

If the data that you want to store into your eXo Cache instance takes a lot of time to load and/or you would like to prevent multiple concurrent loading of the same data at the same time, you can use org.exoplatform.services.cache.future.FutureExoCache on top of your eXo Cache instance in order to delegate the loading of your data to a loader that will be called only once whatever the total amount of concurrent threads looking for it. See below an example of how the FutureExoCache can be used:

    import org.exoplatform.services.cache.future.Loader;

    import org.exoplatform.services.cache.future.FutureExoCache;

    ...

       // Define first your loader and choose properly your context object in order

       // to be able to reuse the same loader for different FutureExoCache instances

       Loader<String, String, String> loader = new Loader<String, String, String>()

       {

          public String retrieve(String context, String key) throws Exception

          {

             return "Value loaded thanks to the key = '" + key + "' and the context = '" + context + "'";

          }

       };

       // Create your FutureExoCache from your eXo cache instance and your loader

       FutureExoCache<String, String, String> myFutureExoCache = new FutureExoCache<String, String, String>(loader, myExoCache);

       // Get your data from your future cache instance

       System.out.println(myFutureExoCache.get("my context", "foo"));

## eXo Cache extension

In the previous versions of eXo kernel, it was quite complex to implement your own ExoCache because it was not open enough. Since kernel 2.0.8, it is possible to easily integrate your favorite cache provider in eXo Products.

You just need to implement your own `ExoCacheFactory` and register it in an eXo container, as described below:

    package org.exoplatform.services.cache;

    ...

    public interface ExoCacheFactory {

      /**

       * Creates a new instance of {@link org.exoplatform.services.cache.ExoCache}

       * @param config the cache to create

       * @return the new instance of {@link org.exoplatform.services.cache.ExoCache}

       * @exception ExoCacheInitException if an exception happens while initializing the cache

       */

      public ExoCache createCache(ExoCacheConfig config) throws ExoCacheInitException;

    }

As you can see, there is only one method to implement which can be seen as a converter of an `ExoCacheConfig` to get an instance of `ExoCache`. Once, you created your own implementation, you can simply register your factory by adding a file conf/portal/configuration.xml with a content of the following type:

    <configuration>

      <component>

        <key>org.exoplatform.services.cache.ExoCacheFactory</key>

        <type>org.exoplatform.tutorial.MyExoCacheFactoryImpl</type>

        ...

      </component>

    </configuration>

### Note

Since kernel 2.3.0-CR1, if the configuration is not a sub class of `ExoCacheConfig` and the implementation given in the configuration is the full qualified name of an existing implementation of eXo Cache, we will assume that the user expects to have an instance of this eXo Cache type so we won't use the configured cache factory.

## eXo Cache based on JBoss Cache

### Configuring the ExoCacheFactory

When you add, the eXo library in your classpath, the eXo service container will use the default configuration provided in the library itself but of course you can still redefined the configuration if you wish as you can do with any components.

The default configuration of the factory is:

    <configuration>

      <component>

        <key>org.exoplatform.services.cache.ExoCacheFactory</key>

        <type>org.exoplatform.services.cache.impl.jboss.ExoCacheFactoryImpl</type>

        <init-params>

          <value-param>

            <name>cache.config.template</name>

            <value>jar:/conf/portal/cache-configuration-template.xml</value>

          </value-param>

          <value-param>

            <name>allow.shareable.cache</name>

            <value>true</value>

          </value-param>

        </init-params>

      </component>

    </configuration>

cache.config.template | This parameter allows you to define the location of the default configuration template of JBoss Cache. In the default configuration, we ask the eXo kernel to get the file shipped into the jar at /conf/portal/cache-configuration-template.xml. The default configuration template aims to be the skeleton from which we will create any type of jboss cache instance, thus it must be very generic. ### Note The default configuration template provided with the jar aims to work with any application servers, but if you intend to use JBoss AS, you should redefine it in your custom configuration to fit better with your AS.
allow.shareable.cache | This parameter allows you to Indicate whether the JBoss Cache instances used can by default be shared between several eXo caches instances. indeed to consume less resources, you can allow to use the same instance of JBoss Cache for several eXo Cache instances, each eXo Cache Instances will have his own cache region with its own eviction configuration. The default value of this parameter is false. ### Note This value is only the default value that cans be redefined at `ExoCacheConfig` level thanks to the field allowShareableCache for more details see the next sections.

### Adding specific configuration for a cache

If for a given reason, you need to use a specific configuration for a cache, you can register one thanks to an "external plugin", see an example below:

    <configuration>

      ...

      <external-component-plugins>

        <target-component>org.exoplatform.services.cache.ExoCacheFactory</target-component>

        <component-plugin>

          <name>addConfig</name>

          <set-method>addConfig</set-method>

          <type>org.exoplatform.services.cache.impl.jboss.ExoCacheFactoryConfigPlugin</type>

          <description>add Custom Configurations</description>

          <init-params>

            <value-param>

              <name>myCustomCache</name>

              <value>jar:/conf/portal/custom-cache-configuration.xml</value>

            </value-param>

          </init-params>

        </component-plugin>

      </external-component-plugins>

      ...

    </configuration>

In the example above, I call the method addConfig(ExoCacheFactoryConfigPlugin plugin) on the current implementation of `ExoCacheFactory` which is actually the jboss cache implementation.

In the init-params block, you can define a set of value-param blocks and for each value-param, we expect the name of cache that needs a specific configuration as name and the location of your custom configuration as value.

In this example, we indicates to the factory that we would like that the cache myCustomCache use the configuration available at jar:/conf/portal/custom-cache-configuration.xml.

### Adding a cache creator

#### Understanding a cache creator

The factory for jboss cache, delegates the cache creation to `ExoCacheCreator` that is defined as below:

    package org.exoplatform.services.cache.impl.jboss;

    ...

    public interface ExoCacheCreator {

      /**

       * Creates an eXo cache according to the given configuration {@link org.exoplatform.services.cache.ExoCacheConfig}

       * @param config the configuration of the cache to apply

       * @param cache the cache to initialize

       * @exception ExoCacheInitException if an exception happens while initializing the cache

       */

      public ExoCache create(ExoCacheConfig config, Cache<Serializable, Object> cache) throws ExoCacheInitException;

      /**

       * Returns the type of {@link org.exoplatform.services.cache.ExoCacheConfig} expected by the creator

       * @return the expected type

       */

      public Class<? extends ExoCacheConfig> getExpectedConfigType();

      /**

       * Returns the name of the implementation expected by the creator. This is mainly used to be backward compatible

       * @return the expected by the creator

       */

      public String getExpectedImplementation();

    }

The `ExoCacheCreator` allows you to define any kind of jboss cache instance that you would like to have. It has been designed to give you the ability to have your own type of configuration and to always be backward compatible.

In an `ExoCacheCreator`, you need to implement 3 methods which are:

- create: this method is used to create a new `ExoCache` from the `ExoCacheConfig` and a jboss cache instance.

- getExpectedConfigType: this method is used to indicate the factory the subtype of `ExoCacheConfig` supported by the creator.

- getExpectedImplementation: this method is used to indicate the factory and the value of field implementation of `ExoCacheConfig` that is supported by the creator. This is used for backward compatibility, in other words, you can still configure your cache with a super class `ExoCacheConfig`.

#### Registering a cache creator

You can register any cache creator that you want thanks to an "external plugin", see an example below:

      <external-component-plugins>

        <target-component>org.exoplatform.services.cache.ExoCacheFactory</target-component>

        <component-plugin>

          <name>addCreator</name>

          <set-method>addCreator</set-method>

          <type>org.exoplatform.services.cache.impl.jboss.ExoCacheCreatorPlugin</type>

          <description>add Exo Cache Creator</description>

          <init-params>

            <object-param>

              <name>LRU</name>

              <description>The lru cache creator</description>

              <object type="org.exoplatform.services.cache.impl.jboss.lru.LRUExoCacheCreator">

                <field name="defaultTimeToLive"><long>1500</long></field>

                <field name="defaultMaxAge"><long>2000</long></field>

              </object>

            </object-param>

          </init-params>

        </component-plugin>

      </external-component-plugins>

In the example above, I call the method addCreator(ExoCacheCreatorPlugin plugin) on the current implementation of `ExoCacheFactory` which is actually the jboss cache implementation.

In the init-params block, you can define a set of object-param blocks and for each object-param, we expect any object definition of type `ExoCacheCreator`.

In this example, we register the action creator related to the eviction policy LRU.

#### The cache creators available

By default, no cache creator are defined, so you need to define them yourself by adding them in your configuration files.

##### LRU Cache Creator - Least Recently Used

    ..

    <object-param>

      <name>LRU</name>

      <description>The lru cache creator</description>

      <object type="org.exoplatform.services.cache.impl.jboss.lru.LRUExoCacheCreator">

        <field name="defaultTimeToLive"><long>${my-value}</long></field>

        <field name="defaultMaxAge"><long>${my-value}</long></field>

      </object>

    </object-param>

    ...

defaultTimeToLive | This is the default value of the field timeToLive described in the section dedicated to this cache type. This value is only used when we define a cache of this type with the old configuration.
defaultMaxAge | This is the default value of the field maxAge described in the section dedicated to this cache type. This value is only used when we define a cache of this type with the old configuration.

##### FIFO Cache Creator - First In, First Out

    ...

    <object-param>

     <name>FIFO</name>

     <description>The fifo cache creator</description>

     <object type="org.exoplatform.services.cache.impl.jboss.fifo.FIFOExoCacheCreator"></object>

    </object-param>

    ...

##### MRU Cache Creator - Most Recently Used

    ...

    <object-param>

      <name>MRU</name>

      <description>The mru cache creator</description>

      <object type="org.exoplatform.services.cache.impl.jboss.mru.MRUExoCacheCreator"></object>

    </object-param>

    ...

##### LFU Cache Creator - Least Frequently Used

    ...

    <object-param>

      <name>LFU</name>

      <description>The lfu cache creator</description>

      <object type="org.exoplatform.services.cache.impl.jboss.lfu.LFUExoCacheCreator">

        <field name="defaultMinNodes"><int>${my-value}</int></field>

      </object>

    </object-param>

    ...

defaultMinNodes | This is the default value of the field minNodes described in the section dedicated to this cache type. This value is only used when we define a cache of this type with the old configuration.

##### EA Cache Creator - Expiration Algorithm

    ...

    <object-param>

      <name>EA</name>

      <description>The ea cache creator</description>

      <object type="org.exoplatform.services.cache.impl.jboss.ea.EAExoCacheCreator">

        <field name="defaultExpirationTimeout"><long>2000</long></field>

      </object>

    </object-param>

    ...

defaultExpirationTimeout | This is the default value of the field minNodes described in the section dedicated to this cache type. This value is only used when we define a cache of this type with the old configuration.

### Defining a cache

#### How to define a cache?

You have 2 ways to define a cache which are:

- At `CacheService` initialization

- With an "external plugin"

##### At `CacheService` initialization

    ...

      <component>

        <key>org.exoplatform.services.cache.CacheService</key>

        <type>org.exoplatform.services.cache.impl.CacheServiceImpl</type>

        <init-params>

          ...

          <object-param>

            <name>fifocache</name>

            <description>The default cache configuration</description>

            <object type="org.exoplatform.services.cache.ExoCacheConfig">

              <field name="name"><string>fifocache</string></field>

              <field name="maxSize"><int>${my-value}</int></field>

              <field name="liveTime"><long>${my-value}</long></field>

              <field name="distributed"><boolean>false</boolean></field>

              <field name="implementation"><string>org.exoplatform.services.cache.FIFOExoCache</string></field>

            </object>

          </object-param>

          ...

        </init-params>

      </component>

    ...

In this example, we define a new cache called fifocache.

##### With an "external plugin"

    ...

      <external-component-plugins>

        <target-component>org.exoplatform.services.cache.CacheService</target-component>

        <component-plugin>

          <name>addExoCacheConfig</name>

          <set-method>addExoCacheConfig</set-method>

          <type>org.exoplatform.services.cache.ExoCacheConfigPlugin</type>

          <description>add ExoCache configuration component plugin </description>

          <init-params>

            ...

            <object-param>

              <name>fifoCache</name>

              <description>The fifo cache configuration</description>

              <object type="org.exoplatform.services.cache.ExoCacheConfig">

                <field name="name"><string>fifocache</string></field>

                <field name="maxSize"><int>${my-value}</int></field>

                <field name="liveTime"><long>${my-value}</long></field>

                <field name="distributed"><boolean>false</boolean></field>

                <field name="implementation"><string>org.exoplatform.services.cache.FIFOExoCache</string></field>

              </object>

            </object-param>

    ...

          </init-params>

        </component-plugin>

      </external-component-plugins>

    ...

In this example, we define a new cache called fifocache which is in fact the same cache as in previous example but defined in a different manner.

#### How to define a distributed or a local cache?

Actually, if you use a custom configuration for your cache as described in a previous section, we will use the cache mode definde in your configuration file.

In case, you decide to use the default configuration template, we use the field distributed of your `ExoCacheConfig` to decide. In other words, if the value of this field is false (the default value), the cache will be a local cache, otherwise it will be the cache mode defined in your default configuration template that should be distributed.

#### How to share a JBoss Cache instance between multiple eXo Cache instances

In order to avoid creating several JBoss Cache instances that consume resources, it is possible to share the same JBoss Cache instance between multiple eXo Cache instances that rely on the same JBoss Cache config. Each eXo Cache instances will then have their own cache region with their own eviction configuration. To allow sharing JBoss Cache instances, you can set the global value at ExoCacheFactory level and if needed set the local value at ExoCacheConfig level knowing that local value will redefine the global value. Each new ExoCacheConfig described below are a sub class of AbstractExoCacheConfig that gives access to the parameter allowShareableCache, if this parameter is set, it will be the value used otherwise it will use the global value. For all the old ExoCacheConfig, only the global value will be used.

#### LRU Cache - Least Recently Used

- New configuration

    ...

           <object-param>

            <name>lru</name>

            <description>The lru cache configuration</description>

            <object type="org.exoplatform.services.cache.impl.jboss.lru.LRUExoCacheConfig">

              <field name="name"><string>lru</string></field>

              <field name="maxNodes"><int>${my-value}</int></field>

              <field name="minTimeToLive"><long>${my-value}</long></field>

              <field name="maxAge"><long>${my-value}</long></field>

              <field name="timeToLive"><long>${my-value}</long></field>

            </object>

          </object-param>

    ...

maxNodes | This is the maximum number of nodes allowed in this region. 0 denotes immediate expiry, -1 denotes no limit.
minTimeToLive | The minimum amount of time (in milliseconds) that a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.
maxAge | Lifespan of a node (in milliseconds) regardless of idle time before the node is swept away. 0 denotes immediate expiry, -1 denotes no limit.
timeToLive | The amount of time that a node is not written to or read (in milliseconds) before the node is swept away. 0 denotes immediate expiry, -1 denotes no limit.

- Old configuration

    ...

          <object-param>

            <name>lru-with-old-config</name>

            <description>The lru cache configuration</description>

            <object type="org.exoplatform.services.cache.ExoCacheConfig">

              <field name="name"><string>lru-with-old-config</string></field>

              <field name="maxSize"><int>${my-value}</int></field>

              <field name="liveTime"><long>${my-value}</long></field>

              <field name="implementation"><string>LRU</string></field>

            </object>

          </object-param>

    ...

maxSize | This is the maximum number of nodes allowed in this region. 0 denotes immediate expiry, -1 denotes no limit.
liveTime | The minimum amount of time (in seconds) that a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.

### Note

For the fields maxAge and timeToLive needed by JBoss cache, we will use the default values provided by the creator.

#### FIFO Cache - First In, First Out

- New configuration

    ...

           <object-param>

            <name>fifo</name>

            <description>The fifo cache configuration</description>

            <object type="org.exoplatform.services.cache.impl.jboss.fifo.FIFOExoCacheConfig">

              <field name="name"><string>fifo</string></field>

              <field name="maxNodes"><int>${my-value}</int></field>

              <field name="minTimeToLive"><long>${my-value}</long></field>

            </object>

          </object-param>

    ...

maxNodes | This is the maximum number of nodes allowed in this region. 0 denotes immediate expiry, -1 denotes no limit.
minTimeToLive | The minimum amount of time (in milliseconds) that a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.

- Old configuration

    ...

           <object-param>

            <name>fifo-with-old-config</name>

            <description>The fifo cache configuration</description>

            <object type="org.exoplatform.services.cache.ExoCacheConfig">

              <field name="name"><string>fifo-with-old-config</string></field>

              <field name="maxSize"><int>${my-value}</int></field>

              <field name="liveTime"><long>${my-value}</long></field>

              <field name="implementation"><string>FIFO</string></field>

            </object>

          </object-param>

    ...

maxSize | This is the maximum number of nodes allowed in this region. 0 denotes immediate expiry, -1 denotes no limit.
liveTime | The minimum amount of time (in seconds) that a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.

#### MRU Cache - Most Recently Used

- New configuration

    ...

           <object-param>

            <name>mru</name>

            <description>The mru cache configuration</description>

            <object type="org.exoplatform.services.cache.impl.jboss.mru.MRUExoCacheConfig">

              <field name="name"><string>mru</string></field>

              <field name="maxNodes"><int>${my-value}</int></field>

              <field name="minTimeToLive"><long>${my-value}</long></field>

            </object>

          </object-param>

    ...

maxNodes | This is the maximum number of nodes allowed in this region. 0 denotes immediate expiry, -1 denotes no limit.
minTimeToLive | The minimum amount of time (in milliseconds) that a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.

- Old configuration

    ...

          <object-param>

            <name>mru-with-old-config</name>

            <description>The mru cache configuration</description>

            <object type="org.exoplatform.services.cache.ExoCacheConfig">

              <field name="name"><string>mru-with-old-config</string></field>

              <field name="maxSize"><int>${my-value}</int></field>

              <field name="liveTime"><long>${my-value}</long></field>

              <field name="implementation"><string>MRU</string></field>

            </object>

          </object-param>

    ...

maxSize | This is the maximum number of nodes allowed in this region. 0 denotes immediate expiry, -1 denotes no limit.
liveTime | The minimum amount of time (in seconds) that a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.

#### LFU Cache - Least Frequently Used

- New configuration

    ...

           <object-param>

            <name>lfu</name>

            <description>The lfu cache configuration</description>

            <object type="org.exoplatform.services.cache.impl.jboss.lfu.LFUExoCacheConfig">

              <field name="name"><string>lfu</string></field>

              <field name="maxNodes"><int>${my-value}</int></field>

              <field name="minNodes"><int>${my-value}</int></field>

              <field name="minTimeToLive"><long>${my-value}</long></field>

            </object>

          </object-param>

    ...

maxNodes | This is the maximum number of nodes allowed in this region. 0 denotes immediate expiry, -1 denotes no limit.
minNodes | This is the minimum number of nodes allowed in this region. This value determines what the eviction queue should prune down to per pass. e.g. If minNodes is 10 and the cache grows to 100 nodes, the cache is pruned down to the 10 most frequently used nodes when the eviction timer makes a pass through the eviction algorithm.
minTimeToLive | The minimum amount of time (in milliseconds) that a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.

- Old configuration

    ...

          <object-param>

            <name>lfu-with-old-config</name>

            <description>The lfu cache configuration</description>

            <object type="org.exoplatform.services.cache.ExoCacheConfig">

              <field name="name"><string>lfu-with-old-config</string></field>

              <field name="maxSize"><int>${my-value}</int></field>

              <field name="liveTime"><long>${my-value}</long></field>

              <field name="implementation"><string>LFU</string></field>

            </object>

          </object-param>

    ...

maxSize | This is the maximum number of nodes allowed in this region. 0 denotes immediate expiry, -1 denotes no limit.
liveTime | The minimum amount of time (in milliseconds) that a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.

### Note

For the fields minNodes and timeToLive needed by JBoss cache, we will use the default values provided by the creator.

#### EA Cache - Expiration Algorithm

- New configuration

    ...

           <object-param>

            <name>ea</name>

            <description>The ea cache configuration</description>

            <object type="org.exoplatform.services.cache.impl.jboss.ea.EAExoCacheConfig">

              <field name="name"><string>ea</string></field>

              <field name="maxNodes"><int>${my-value}</int></field>

              <field name="minTimeToLive"><long>${my-value}</long></field>

              <field name="expirationTimeout"><long>${my-value}</long></field>

            </object>

          </object-param>

    ...

maxNodes | This is the maximum number of nodes allowed in this region. 0 denotes immediate expiry, -1 denotes no limit.
minTimeToLive | The minimum amount of time (in milliseconds) that a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.
expirationTimeout | This is the timeout after which the cache entry must be evicted.

- Old configuration

    ...

          <object-param>

            <name>ea-with-old-config</name>

            <description>The ea cache configuration</description>

            <object type="org.exoplatform.services.cache.ExoCacheConfig">

              <field name="name"><string>lfu-with-old-config</string></field>

              <field name="maxSize"><int>${my-value}</int></field>

              <field name="liveTime"><long>${my-value}</long></field>

              <field name="implementation"><string>EA</string></field>

            </object>

          </object-param>

    ...

maxSize | This is the maximum number of nodes allowed in this region. 0 denotes immediate expiry, -1 denotes no limit.
liveTime | The minimum amount of time (in milliseconds) that a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.

### Note

For the fields expirationTimeout needed by JBoss cache, we will use the default values provided by the creator.

## eXo Cache based on Infinispan

### Configure the ExoCacheFactory

When you add the related jar file in your classpath, the eXo service container will use the default configuration provided in the library itself but of course you can still redefined the configuration if you wish as you can do with any components.

The default configuration of the factory is:

    <configuration>

      <component>

        <key>org.exoplatform.services.cache.ExoCacheFactory</key>

        <type>org.exoplatform.services.cache.impl.infinispan.ExoCacheFactoryImpl</type>

        <init-params>

          <value-param>

            <name>cache.config.template</name>

            <value>jar:/conf/portal/cache-configuration-template.xml</value>

          </value-param>

        </init-params>

      </component>

    </configuration>

As you can see the factory requires one single parameter which is cache.config.template, this parameter allows you to define the location of the default configuration template of your infinispan. In the default configuration, we ask the eXo container to get the file shipped into the jar at /conf/portal/cache-configuration-template.xml.

The default configuration template aims to be the skeleton from which we will create any type of infinispan cache instance, thus it must be very generic.

### Note

All the cache instances fo which we configure the same cluster name will also share the same `EmbeddedCacheManager.`

### Add specific configuration for a cache

If for a given reason, you need to use a specific configuration for a cache, you can register one thanks to an "external plugin", see an example below:

    <configuration>

      ...

      <external-component-plugins>

        <target-component>org.exoplatform.services.cache.ExoCacheFactory</target-component>

        <component-plugin>

          <name>addConfig</name>

          <set-method>addConfig</set-method>

          <type>org.exoplatform.services.cache.impl.infinispan.ExoCacheFactoryConfigPlugin</type>

          <description>add Custom Configurations</description>

          <init-params>

            <value-param>

              <name>myCustomCache</name>

              <value>jar:/conf/portal/custom-cache-configuration.xml</value>

            </value-param>

          </init-params>

        </component-plugin>

      </external-component-plugins>

      ...

    </configuration>

In the example above, I call the method addConfig(ExoCacheFactoryConfigPlugin plugin) on the current implementation of `ExoCacheFactory` which is actually the infinispan implementation.

In the init-params block, you can define a set of value-param blocks and for each value-param, we expect the name of cache that needs a specific configuration as name and the location of your custom configuration as value.

In this example, we indicates to the factory that we would like that the cache myCustomCache use the configuration available at jar:/conf/portal/custom-cache-configuration.xml.

### Note

All the cache instances that will rely on the cache configuration located at the same location will share the same `EmbeddedCacheManager`.

### Add a cache creator

#### Understanding a cache creator

The factory for infinispan, delegates the cache creation to `ExoCacheCreator` that is defined as below:

    package org.exoplatform.services.cache.impl.infinispan;

    ...

    public interface ExoCacheCreator {

       /**

        * Creates an eXo cache according to the given configuration {@link org.exoplatform.services.cache.ExoCacheConfig}

        * @param config the configuration of the cache to apply

        * @param confBuilder the configuration builder of the infinispan cache

        * @param cacheGetter a {@link Callable} instance from which we can get the cache

        * @exception ExoCacheInitException if an exception happens while initializing the cache

        */

       public ExoCache<Serializable, Object> create(ExoCacheConfig config, ConfigurationBuilder confBuilder,

                Callable<Cache<Serializable, Object>> cacheGetter) throws ExoCacheInitException;

       /**

        * Returns the type of {@link org.exoplatform.services.cache.ExoCacheConfig} expected by the creator

        * @return the expected type

        */

       public Class<? extends ExoCacheConfig> getExpectedConfigType();

       /**

        * Returns a set of all the implementations expected by the creator. This is mainly used to be backward compatible

        * @return the expected by the creator

        */

       public Set<String> getExpectedImplementations();

    }

The `ExoCacheCreator` allows you to define any kind of infinispan cache instance that you would like to have. It has been designed to give you the ability to have your own type of configuration and to always be backward compatible.

In an `ExoCacheCreator`, you need to implement 3 methods which are:

- create - this method is used to create a new `ExoCache` from the `ExoCacheConfig`, an inifinispan cache configuration and a Callable object to allow you to get the cache instance.

- getExpectedConfigType - this method is used to indicate the factory the subtype of `ExoCacheConfig` supported by the creator.

- getExpectedImplementations - this method is used to indicate the factory the values of the field implementation of `ExoCacheConfig` that is supported by the creator. This is used for backward compatibility, in other words you can still configure your cache with an instance of `ExoCacheConfig`.

#### Register a cache creator

You can register any cache creator you want thanks to an "external plugin", see an example below:

      <external-component-plugins>

        <target-component>org.exoplatform.services.cache.ExoCacheFactory</target-component>

        <component-plugin>

          <name>addCreator</name>

          <set-method>addCreator</set-method>

          <type>org.exoplatform.services.cache.impl.infinispan.ExoCacheCreatorPlugin</type>

          <description>add Exo Cache Creator</description>

          <init-params>

            <object-param>

              <name>Test</name>

              <description>The cache creator for testing purpose</description>

              <object type="org.exoplatform.services.cache.impl.infinispan.TestExoCacheCreator"></object>

            </object-param>

          </init-params>

        </component-plugin>

      </external-component-plugins>

In the example above, I call the method addCreator(ExoCacheCreatorPlugin plugin) on the current implementation of `ExoCacheFactory` which is actually the infinispan implementation.

In the init-params block, you can define a set of object-param blocks and for each object-param, we expect any object definition of type `ExoCacheCreator`.

In this example, we register the cache creator related to the eviction policy Test.

#### The cache creators available

By default, no cache creator are defined, so you need to define them yourself by adding them in your configuration files.

##### Generic Cache Creator

This is the generic cache creator that allows you to use any eviction strategies defined by default in Infinispan.

    ..

    <object-param>

      <name>GENERIC</name>

      <description>The generic cache creator</description>

      <object type="org.exoplatform.services.cache.impl.infinispan.generic.GenericExoCacheCreator">

        <field name="implementations">

          <collection type="java.util.HashSet">

             <value>

                <string>NONE</string>

             </value>

             <value>

                <string>FIFO</string>

             </value>

             <value>

                <string>LRU</string>

             </value>

             <value>

                <string>UNORDERED</string>

             </value>

             <value>

                <string>LIRS</string>

             </value>

          </collection>

        </field>

        <field name="defaultStrategy"><string>${my-value}</string></field>

        <field name="defaultMaxIdle"><long>${my-value}</long></field>

        <field name="defaultWakeUpInterval"><long>${my-value}</long></field>

      </object>

    </object-param>

    ...

implementations | This is the list of all the implementations supported by the cache creator. Actualy, it is a subset of the full list of the eviction strategies supported by infinispan to which you want to give access to. In the configuraton above, you have the full list of all the eviction strategies currently supported by infinispan 4.1. This field is used to manage the backward compatibility.
defaultStrategy | This is the name of the default eviction strategy to use. By default the value is LRU. This value is only use when we define a cache of this type with the old configuration.
defaultMaxIdle | This is the default value of the field maxIdle described in the section dedicated to this cache type. By default the value is -1.This value is only use when we define a cache of this type with the old configuration.
defaultWakeUpInterval | his is the default value of the field wakeUpInterval described in the section dedicated to this cache type. By default the value is 5000.This value is only use when we define a cache of this type with the old configuration

### Define an infinispan cache instance

#### How to define a replicated, a distributed or a local cache?

Actually, if you use a custom configuration for your cache as described in a previous section, we will use the cache mode define in your configuration file.

In case, you decide to use the default configuration template, we use the fields distributed and replicated of your `ExoCacheConfig` to decide. In other words, if the value of these fields is false (the default value), the cache will be a local cache otherwise if the field distributed is set to true, the cache will be then be considered as distrbuted and will be retrieved from the DistributedCacheManager (more details about it in the next chapter). Finally if the field replicated is set to true, the cache mode of your cache will be the one defined in the configuration assuming that it should be replicated.

#### How to define an infinispan cache instance

All the eviction strategies proposed by default in infinispan rely on the generic cache creator.

- New configuration

    ...

           <object-param>

            <name>myCache</name>

            <description>My cache configuration</description>

            <object type="org.exoplatform.services.cache.impl.infinispan.generic.GenericExoCacheConfig">

              <field name="name"><string>myCacheName</string></field>

              <field name="strategy"><int>${my-value}</int></field>

              <field name="maxEntries"><long>${my-value}</long></field>

              <field name="lifespan"><long>${my-value}</long></field>

              <field name="maxIdle"><long>${my-value}</long></field>

              <field name="wakeUpInterval"><long>${my-value}</long></field>

            </object>

          </object-param>

    ...

strategy | The name of the strategy to use such as 'UNORDERED', 'FIFO', 'LRU', 'LIRS' and 'NONE' (to disable eviction).
maxEntries | Maximum number of entries in a cache instance. If selected value is not a power of two the actual value will default to the least power of two larger than selected value. -1 means no limit which is also the default value.
lifespan | Maximum lifespan of a cache entry, after which the entry is expired cluster-wide, in milliseconds. -1 means the entries never expire which is also the default value.
maxIdle | Maximum idle time a cache entry will be maintained in the cache, in milliseconds. If the idle time is exceeded, the entry will be expired cluster-wide. -1 means the entries never expire which is also the default value.
wakeUpInterval | Interval between subsequent eviction runs, in milliseconds. If you wish to disable the periodic eviction process altogether, set wakeupInterval to -1. The default value is 5000.

- Old configuration

    ...

          <object-param>

            <name>myCache</name>

            <description>My cache configuration</description>

              <field name="name"><string>lru-with-old-config</string></field>

              <field name="maxSize"><int>${my-value}</int></field>

              <field name="liveTime"><long>${my-value}</long></field>

              <field name="implementation"><string>${my-value}</string></field>

            </object>

          </object-param>

    ...

maxSize | Maximum number of entries in a cache instance. If selected value is not a power of two the actual value will default to the least power of two larger than selected value. -1 means no limit which is also the default value.
liveTime | Maximum lifespan of a cache entry, after which the entry is expired cluster-wide, in milliseconds. -1 means the entries never expire which is also the default value.
implementation | The name of the implementation to use the expected value is one of the eviction strategies defined in the field implementations of the generic cache creator.

### Note

For the fields maxIdle and wakeUpInterval needed by infinispan, we will use the default values provided by the creator.

### Using Infinispan in distributed mode

In order to be able to use infinispan in distributed mode with the ability to launch external JVM instances that will manage a part of the cache, we need to configure the DistributedCacheManager. In the next sections, we will show how to configure the component and how to launch external JVM instances.

#### Configuration of the DistributedCacheManager

The DistributedCacheManager is the component that will manage all the cache instances that we expect to be distributed, it must be unique in the whole JVM which means that it must be declared at RootContainer level in portal mode or at StandaloneContainer in standalone mode. See below an example of configuration.

    <component>

      <type>org.exoplatform.services.ispn.DistributedCacheManager</type>

      <init-params>

        <value-param>

          <name>infinispan-configuration</name>

          <value>jar:/conf/distributed-cache-configuration.xml</value>

        </value-param>

        <properties-param>

          <name>parameters</name>

          <description>The parameters of the configuration</description>

          <property name="configurationFile" value="${gatein.jcr.jgroups.config}"></property>

          <property name="invalidationThreshold" value="0"></property>

          <property name="numOwners" value="3"></property>

          <property name="numVirtualNodes" value="2"></property>

        </properties-param>

      </init-params>

    </component>

infinispan-configuration | Location of the infinispan configuration to use in which all the distributed caches must be configured by name. All paths supported by the ConfigurationManager with prefixes like jar:/..., classpath:/..., etc. This parameter is mandatory.
parameters | In the configuration file, you can add a set of variables of type ${variable-name}, these variables will be solved using the values of the parameters. The name of the variable in the configuration file must match with the name of the parameter. This parameter is optional.

As described above, the configuration of infinispan must defined explicitly each cache using the nameCache block no dynamic configuration of cache is supported. Indeed to ensure that the whole cluster is consistent in term of defined cache, it is required to configure all the cache that you will need and register it using its future name.

For now, we have 2 supported cache name which are JCRCache and eXoCache. JCRCache is the name of the cache that we use in case we would like to store the data of the JCR into a distributed cache. eXoCache is the name of the cache that we use in case we would like to store the data of some eXo Cache instances into a distributed cache.

See below an example of infinispan configuration with both eXoCache and JCRCache defined:

    <infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:infinispan:config:5.1 http://www.infinispan.org/schemas/infinispan-config-5.1.xsd"
      xmlns="urn:infinispan:config:5.1">
       <global>
          <globalJmxStatistics jmxDomain="exo" enabled="true" allowDuplicateDomains="true"/>
          <transport transportClass="org.infinispan.remoting.transport.jgroups.JGroupsTransport" clusterName="JCR-cluster" distributedSyncTimeout="20000">
            <properties>
              <property name="configurationFile" value="${configurationFile}"/>
            </properties>
          </transport>
          <shutdown hookBehavior="DEFAULT"/>
       </global>
       <namedCache name="JCRCache">
          <locking isolationLevel="READ_COMMITTED" lockAcquisitionTimeout="120000" writeSkewCheck="false" concurrencyLevel="500" useLockStriping="true" />
          <transaction transactionManagerLookupClass="org.infinispan.transaction.lookup.GenericTransactionManagerLookup" syncRollbackPhase="true" syncCommitPhase="true" eagerLockSingleNode="true" transactionMode="TRANSACTIONAL"/>
          <jmxStatistics enabled="true"/>
          <clustering mode="distribution">
            <l1 enabled="true" invalidationThreshold="${invalidationThreshold}"/>
             <hash numOwners="${numOwners}" numVirtualNodes="${numVirtualNodes}" rehashRpcTimeout="120000">
               <groups enabled="true"/>
             </hash>
             <sync replTimeout="180000"/>
          </clustering>
       </namedCache>
       <namedCache name="eXoCache">
          <locking isolationLevel="READ_COMMITTED" lockAcquisitionTimeout="120000" writeSkewCheck="false" concurrencyLevel="500" useLockStriping="true" />
          <transaction transactionManagerLookupClass="org.infinispan.transaction.lookup.GenericTransactionManagerLookup" syncRollbackPhase="true" syncCommitPhase="true" eagerLockSingleNode="true" transactionMode="TRANSACTIONAL"/>
          <jmxStatistics enabled="true"/>
          <clustering mode="distribution">
             <l1 enabled="true" invalidationThreshold="${invalidationThreshold}"/>
             <hash numOwners="${numOwners}" numVirtualNodes="${numVirtualNodes}" rehashRpcTimeout="120000"/>
             <sync replTimeout="180000"/>
          </clustering>
       </namedCache>
    </infinispan>

#### Launch a CacheServer

In case you intend to use the distribued mode, you can launch external JVM in standalone mode to provide more memory to your current cache. To do so, you will need to get the file of type exo.jcr.component.core.impl.infinispan.v5-binary.zip in which you will find scripts to launch your cache servers. These scripts allow optional arguments that are described below:

help|?|<configuration-file-path>|udp|tcp <initial-hosts>

help | Print the expected syntax.
? | Print the expected syntax.
configuration-file-path | The location of the configuration file to use, we expect an absolute path. It will try to get it using the current class loader, if it cannot be found it will get it from the file system. By default it will use the path /conf/cache-server-configuration.xml that is actually a file bundled into the jar.
udp | We use this parameter value when we want to use the default configuration file with udp as transport stack which is actually the default stack used which means that it will have the exact same behavior as when we don't provide any parameter.
tcp | We use this parameter value when we want to use the default configuration file with tcp as transport stack.
initial-hosts | This parameter is optional and is only allowed in case the tcp stack is enabled, it will allow you to define the set of hosts that will be part of the cluster. The syntax of this parameter is a list of hostname[port] comma-separated. Knowing that the default value is "localhost[7800],localhost[7801]" if this parameter is not set, the bind address will be automatically set to 127.0.0.1 so you need to ensure that your server is configured to have it mapped to localhost.

### Note

If you intend to use the CacheServer in order to manage some of your eXo Cache instances, don't forget to add the jar files that define both the keys and the values in the lib directory of the CacheServer distribution and restarts your CacheServer instances otherwise the unmarshalling will fail with java.lang.ClassNotFoundException.

#### Configure the cache of your workspace

In case you would like to configure your workspace in order to rely on a distributed cache apart using org.exoplatform.services.jcr.impl.dataflow.persistent.infinispan.ISPNCacheWorkspaceStorageCache as FQN of your cache you will need to set the property use-distributed-cache to true. If you do so the JCR cache will rely on the cache called JCRCache defined in the infinispan configuration provided to the DistributedCacheManager.

Copyright © 2009-2012. All rights reserved. eXo Platform SAS