Configuring Ehcache

In order to start using Ehcache, you will need to configure your first CacheManager and Cache. This can be achieved through programmatic configuration or XML.

If you are looking to use the JSR-107, aka javax.cache API, you should start by reading the Ehcache 3.x JSR-107 Provider page.

Configuring with Java

Java configuration is most easily achieved through the use of builders that offer a fluent API.

Managed cache

As with the previous versions of Ehcache, the canonical way of dealing with Cache is through a CacheManager:

CacheManager cacheManager
    = CacheManagerBuilder.newCacheManagerBuilder() (1)
    .withCache("preConfigured",
        CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) (2)
    .build(); (3)
cacheManager.init(); (4)

Cache<Long, String> preConfigured =
    cacheManager.getCache("preConfigured", Long.class, String.class); (5)

Cache<Long, String> myCache = cacheManager.createCache("myCache", (6)
    CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10)).build());

myCache.put(1L, "da one!"); (7)
String value = myCache.get(1L); (8)

cacheManager.removeCache("preConfigured"); (9)

cacheManager.close(); (10)
1 The static method org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder returns a new org.ehcache.config.builders.CacheManagerBuilder instance.
2 Use the builder to define a Cache with alias "preConfigured". This cache will be created when cacheManager.build() is invoked on the actual CacheManager instance. The first String argument is the cache alias, which is used to retrieve the cache from the CacheManager. The second argument, org.ehcache.config.CacheConfiguration, is used to configure the Cache. We use the static newCacheConfigurationBuilder() method on org.ehcache.config.builders.CacheConfigurationBuilder to create a default configuration.
3 Finally, invoking build() returns a fully instantiated, but uninitialized, CacheManager we can use.
4 Before using the CacheManager it needs to be initialized, which can be done in 1 of 2 ways: Calling CacheManager.init() on the CacheManager instance, or calling the CacheManagerBuilder.build(boolean init) method with the boolean parameter set to true.
5 A cache is retrieved by passing its alias, key type and value type to the CacheManager. For instance, to obtain the cache declared in step 2 you need its alias=preConfigured, keyType=Long.class and valueType=String.class. For type-safety, we ask for both key and value types to be passed in. If these differ from the ones we expect, the CacheManager throws a ClassCastException early in the application’s lifecycle. This guards the Cache from being polluted by random types.
6 The CacheManager can be used to create new Cache instances as needed. Just as in step 2, it requires passing in an alias as well as a CacheConfiguration. The instantiated and fully initialized Cache added will be returned and/or accessed through the CacheManager.getCache API.
7 The newly added Cache can now be used to store entries, which are comprised of key value pairs. The put method’s first parameter is the key and the second parameter is the value. Remember the key and value types must be the same types as those defined in the CacheConfiguration. Additionally the key must be unique and is only associated with one value.
8 A value is retrieved from a cache by calling the cache.get(key) method. It only takes one parameter which is the key, and returns the value associated with that key. If there is no value associated with that key then null is returned.
9 We can CacheManager.removeCache(String) a given Cache. The CacheManager will not only remove its reference to the Cache, but will also close it. The Cache releases all locally held transient resources (such as memory). References to this Cache become unusable.
10 In order to release all transient resources (memory, threads, …​) a CacheManager provides to Cache instances it manages, you have to invoke CacheManager.close(), which in turns closes all Cache instances known at the time.

Creating a cache manager with clustering support

To enable Clustering with Terracotta, firstly you will have to start the Terracotta server configured with clustered storage. In addition, for creating the cache manager with clustering support, you will need to provide the clustering service configuration:

final CacheManagerBuilder<PersistentCacheManager> clusteredCacheManagerBuilder =
    CacheManagerBuilder.newCacheManagerBuilder() (1)
        .with(ClusteringServiceConfigurationBuilder.cluster(URI.create("terracotta://localhost:9510/my-application")) (2)
            .autoCreate()); (3)
final PersistentCacheManager cacheManager = clusteredCacheManagerBuilder.build(true); (4)

cacheManager.close(); (5)
1 Returns the org.ehcache.config.builders.CacheManagerBuilder instance;
2 Use the ClusteringServiceConfigurationBuilder's static method .cluster(URI) for connecting the cache manager to the clustering storage at the URI specified that returns the clustering service configuration builder instance. The sample URI provided in the example points to the clustered storage with clustered storage identifier my-application on the Terracotta server (assuming the server is running on localhost and port 9510); the query-param auto-create creates the clustered storage in the server if it doesn’t already exist.
3 Returns a fully initialized cache manager that can be used to create clustered caches.
4 Close the cache manager.
See the clustered cache documentation for more information on this feature.

User managed cache

Ehcache 3 introduces the concept of UserManagedCache:

UserManagedCache<Long, String> userManagedCache =
    UserManagedCacheBuilder.newUserManagedCacheBuilder(Long.class, String.class)
        .build(false); (1)
userManagedCache.init(); (2)

userManagedCache.put(1L, "da one!"); (3)

userManagedCache.close(); (4)
1 A new feature of Ehcache 3 is the ability to create UserManagedCache instances, i.e. ones not managed by a CacheManager, again you can either have the builder init() it for you, passing true, or
2 pass false and it is up to you to UserManagedCache.init() them, prior to using them.
3 You can use the cache exactly as a managed cache
4 In the same vein, a UserManagedCache requires you to UserManagedCache.close() it explicitly. If you would also use managed caches simultaneously, the CacheManager.close() operation would not impact the user managed cache(s).
See the user managed cache documentation for more information on this feature.

Storage Tiers

Ehcache 3, as in previous versions, offers a tiering model to allow storing increasing amounts of data on slower tiers (which are generally more abundant).

The idea is that resources related to faster storage are more rare, but are located where the 'hottest' data is preferred to be. Thus less-hot (less frequently used) data is moved to the more abundant but slower tiers. Hotter data is moved onto the faster tiers.

Off-heap

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("tieredCache",
    CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
        ResourcePoolsBuilder.newResourcePoolsBuilder()
            .heap(10, EntryUnit.ENTRIES)
            .offheap(10, MemoryUnit.MB)) (1)
        )
    .build(true);

cacheManager.close();
1 If you wish to use off-heap, you’ll have to define a resource pool, giving the memory size you want to allocate.

The example above allocates a very small amount of off-heap. Remember that data stored off-heap will have to be serialized and deserialized - and is thus slower than heap. You should thus favor off-heap for large amounts of data where on-heap would have too severe an impact on garbage collection.

Do not forget to define in the java options the -XX:MaxDirectMemorySize option, according to the off-heap size you intend to use.

Disk persistence

PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
    .with(CacheManagerBuilder.persistence(getStoragePath() + File.separator + "myData")) (1)
    .withCache("persistent-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
        ResourcePoolsBuilder.newResourcePoolsBuilder()
            .heap(10, EntryUnit.ENTRIES)
            .disk(10, MemoryUnit.MB, true)) (2)
        )
    .build(true);

persistentCacheManager.close();
1 If you wish to use disk storage (like for persistent Cache instances), you’ll have to provide a location where data should be stored on disk to the CacheManagerBuilder.persistence(String) static method.
2 Defines a resource pool for the disk. The third parameter is a boolean value which is used to set whether the disk pool is persistent. When set to true, the pool is persistent, and when set to false, the pool is not persistent. When this method is used without the third boolean parameter then the pool is not persistent.

The example above allocates a very small amount of disk storage. Remember that data stored on disk will have to be serialized / deserialized and written / read from disk - and is thus slower than heap and offheap. You should thus favor disk for large amounts of data.

Another reason to use disk storage is persistence across application restarts. Note that Ehcache 3 only offers persistence in the case of clean shutdowns.

Three tiers

The example below illustrates how to use disk storage for a non-persistent Cache instance.

PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
    .with(CacheManagerBuilder.persistence(getStoragePath() + File.separator + "myData")) (1)
    .withCache("threeTieredCache",
        CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
            ResourcePoolsBuilder.newResourcePoolsBuilder()
                .heap(10, EntryUnit.ENTRIES) (2)
                .offheap(1, MemoryUnit.MB) (3)
                .disk(20, MemoryUnit.MB) (4)
            )
    ).build(true);

persistentCacheManager.close();
1 If you wish to use disk storage (like for persistent Cache instances), you’ll have to provide a location where data should be stored on disk to the CacheManagerBuilder.persistence(String) static method.
2 You define a resource pool for the heap.
3 You define a resource pool for the off-heap.
4 You define a non-persistent resource pool for the disk. Remember this pool is not persistent because we declared the disk pool using the method that does not use the boolean persistent parameter.

Byte-sized heap

You can also size the heap tier using memory units instead of entry count.

Byte sizing has a runtime performance impact that depends on the size and graph complexity of the data cached.
CacheConfiguration<Long, String> usesConfiguredInCacheConfig = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
        ResourcePoolsBuilder.newResourcePoolsBuilder()
            .heap(10, MemoryUnit.KB) (1)
            .offheap(10, MemoryUnit.MB))
    .withSizeOfMaxObjectGraph(1000)
    .withSizeOfMaxObjectSize(1000, MemoryUnit.B) (2)
    .build();

CacheConfiguration<Long, String> usesDefaultSizeOfEngineConfig = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
        ResourcePoolsBuilder.newResourcePoolsBuilder()
            .heap(10, MemoryUnit.KB)
            .offheap(10, MemoryUnit.MB))
    .build();

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
    .withDefaultSizeOfMaxObjectSize(500, MemoryUnit.B)
    .withDefaultSizeOfMaxObjectGraph(2000) (3)
    .withCache("usesConfiguredInCache", usesConfiguredInCacheConfig)
    .withCache("usesDefaultSizeOfEngine", usesDefaultSizeOfEngineConfig)
    .build(true);

Cache<Long, String> usesConfiguredInCache = cacheManager.getCache("usesConfiguredInCache", Long.class, String.class);

usesConfiguredInCache.put(1L, "one");
assertThat(usesConfiguredInCache.get(1L), equalTo("one"));

Cache<Long, String> usesDefaultSizeOfEngine = cacheManager.getCache("usesDefaultSizeOfEngine", Long.class, String.class);

usesDefaultSizeOfEngine.put(1L, "one");
assertThat(usesDefaultSizeOfEngine.get(1L), equalTo("one"));

cacheManager.close();
1 You can also size the heap tier in bytes. This will limit the amount of memory used by the heap tier for storing key-value pairs. Note that there is a cost associated to sizing objects.
2 The sizing can also be further restrained by 2 additional configuration settings: The first one specifies the maximum number of objects to traverse while walking the object graph, the second defines the maximum size of a single object. If the sizing goes above any of these two limits, the mutative operation on the cache will be ignored.
3 A default configuration can be provided at CacheManager level to be used by the caches unless defined explicitly.

Update ResourcePools

Limited size adjustment can be performed on a live cache.

updateResourcePools() only allows you to change the heap tier sizing, not the pool type. Thus you can’t change the sizing of off-heap or disk tiers.
ResourcePools pools = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(20L, EntryUnit.ENTRIES).build(); (1)
cache.getRuntimeConfiguration().updateResourcePools(pools); (2)
assertThat(cache.getRuntimeConfiguration().getResourcePools()
    .getPoolForResource(ResourceType.Core.HEAP).getSize(), is(20L));
1 You will need to create a new ResourcePools object with resources of required size, using ResourcePoolsBuilder. This object can then be passed to the said method so as to trigger the update.
2 To update capacity of ResourcePools, the updateResourcePools(ResourcePools) method in RuntimeConfiguration can be of help. The ResourcePools object created earlier can then be passed to this method so as to trigger the update.

Data freshness

In Ehcache, data freshness is controlled through Expiry. The following illustrates how to configure a time-to-live expiry.

CacheConfiguration<Long, String> cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
        ResourcePoolsBuilder.heap(100)) (1)
    .withExpiry(Expirations.timeToLiveExpiration(Duration.of(20, TimeUnit.SECONDS))) (2)
    .build();
1 Expiry is configured at the cache level, so start by defining a cache configuration,
2 then add to it an Expiry, here using the predefined time-to-live one, configured with the required Duration.

See the section on expiry for more information about the options available.

Configuring With XML

…​It wouldn’t be Java without some XML.

You can create an XML file to configure a CacheManager:

  <cache alias="foo"> (1)
    <key-type>java.lang.String</key-type> (2)
    <resources>
      <heap unit="entries">2000</heap> (3)
      <offheap unit="MB">500</offheap> (4)
    </resources>
  </cache>

  <cache-template name="myDefaults"> (5)
    <key-type>java.lang.Long</key-type>
    <value-type>java.lang.String</value-type>
    <heap unit="entries">200</heap>
  </cache-template>

  <cache alias="bar" uses-template="myDefaults"> (6)
    <key-type>java.lang.Number</key-type>
  </cache>

  <cache alias="simpleCache" uses-template="myDefaults" /> (7)
1 Declares a Cache aliased to foo
2 The keys of foo are declared as type String; since the value type is not specified, the values will be of type Object.
3 foo is declared to hold up to 2,000 entries on heap…​
4 …​as well as up to 500 MB of off-heap memory before it starts evicting
5 <cache-template> elements let you create an abstract configuration that further <cache> configurations can then extend
6 bar is such a Cache. bar uses the <cache-template> named myDefaults and overrides its key-type to a wider type.
7 simpleCache is another such Cache. It uses myDefaults configuration for its sole CacheConfiguration.

Refer to the XML documentation for more details on the XML format.

In order to parse an XML configuration, you can use the XmlConfiguration type:

final URL myUrl = this.getClass().getResource("/my-config.xml"); (1)
Configuration xmlConfig = new XmlConfiguration(myUrl); (2)
CacheManager myCacheManager = CacheManagerBuilder.newCacheManager(xmlConfig); (3)
1 Obtain a URL to your XML file’s location
2 Instantiate an XmlConfiguration passing the XML file’s URL to it
3 Using the static org.ehcache.config.builders.CacheManagerBuilder.newCacheManager(org.ehcache.config.Configuration) lets you create your CacheManager instance using the Configuration from the XmlConfiguration

Current development

For developer information, you might want to check the Ehcache 3 project wiki on GitHub.

The next version, 3.2, will finalize Terracotta clustering support. Cache events, cache-through and transactional clustered caches will then be supported.