Getting started with Ehcache 3 & JSR-107

There is nothing special for you to do to use Ehcache 3 as the caching provider for your application: add the ehcache-3.1.0.jar to your application’s classpath (possibly removing the previous provider’s jar) and you are ready to go:

CachingProvider provider = Caching.getCachingProvider(); (1)
CacheManager cacheManager = provider.getCacheManager(); (2)
1 Retrieves the default CachingProvider, this should be org.ehcache.jsr107.EhcacheCachingProvider. You can also force use of org.ehcache.jsr107.EhcacheCachingProvider by using the Caching.getCachingProvider(String) static method instead.
2 Retrieve the default CacheManager instance using the provider.
See the section on using Ehcache 3 and JSR-107 for details on required libraries.

JSR-107 and Ehcache configuration integration

Starting from JSR-107 created caches

When you create a Cache on a CacheManager using a MutableConfiguration - that is you only use JSR-107 types - you can still get to the underlying Ehcache RuntimeCacheConfiguration:

MutableConfiguration<Long, String> configuration = new MutableConfiguration<Long, String>();
configuration.setTypes(Long.class, String.class);
Cache<Long, String> cache = cacheManager.createCache("someCache", configuration); (1)

CompleteConfiguration<Long, String> completeConfiguration = cache.getConfiguration(CompleteConfiguration.class); (2)

Eh107Configuration<Long, String> eh107Configuration = cache.getConfiguration(Eh107Configuration.class); (3)

CacheRuntimeConfiguration<Long, String> runtimeConfiguration = eh107Configuration.unwrap(CacheRuntimeConfiguration.class); (4)
1 Create a JSR-107 cache using the MutableConfiguration from the specification
2 Get to the JSR-107 CompleteConfiguration
3 Get to the Ehcache JSR-107 configuration bridge
4 Unwrap to the Ehcache RuntimeCacheConfiguration type

Building the configuration using Ehcache APIs

You can also create a JSR-107 Cache using an Ehcache CacheConfiguration. When using this mechanism, no JSR-107 CompleteConfiguration is used and so you cannot get to one.

CacheConfiguration<Long, String> cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
    ResourcePoolsBuilder.heap(10)).build(); (1)

Cache<Long, String> cache = cacheManager.createCache("myCache",
    Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration)); (2)

Eh107Configuration<Long, String> configuration = cache.getConfiguration(Eh107Configuration.class);
configuration.unwrap(CacheConfiguration.class); (3)

configuration.unwrap(CacheRuntimeConfiguration.class); (4)

try {
  cache.getConfiguration(CompleteConfiguration.class); (5)
  throw new AssertionError("IllegalArgumentException expected");
} catch (IllegalArgumentException iaex) {
  // Expected
}
1 Create an Ehcache CacheConfiguration - through a builder as shown here or even through XML
2 Use the configuration with JSR-107 API by wrapping it
3 Get back to the Ehcache CacheConfiguration …​
4 or to the runtime configuration even.
5 No JSR-107 CompleteConfiguration is available in this context

Getting JSR-107 caches configured through Ehcache XML

Another way to have the full Ehcache configuration options on your caches while having no code dependency on the provider is to use XML based configuration. See the XML documentation for more details on configuring `Cache`s in XML.

Find below the XML configuration followed by the code to use it from JSR-107:

<config
    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xmlns='http://www.ehcache.org/v3'
    xsi:schemaLocation="
        http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">

  <cache alias="ready-cache">
    <key-type>java.lang.Long</key-type>
    <value-type>com.pany.domain.Product</value-type>
    <loader-writer>
      <class>com.pany.ehcache.integration.ProductCacheLoaderWriter</class>
    </loader-writer>
    <heap unit="entries">100</heap>
  </cache>

</config>
CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager manager = cachingProvider.getCacheManager( (1)
    getClass().getResource("/org/ehcache/docs/ehcache-jsr107-config.xml").toURI(), (2)
    getClass().getClassLoader()); (3)
Cache<Long, Product> readyCache = manager.getCache("ready-cache", Long.class, Product.class); (4)
1 Invoking javax.cache.spi.CachingProvider.getCacheManager(java.net.URI, java.lang.ClassLoader)
2 and passing in a URI that resolves to an Ehcache XLM configuration file.
3 the second argument being the ClassLoader to use to load user-types if needed; i.e. Class instances that are stored in the Cache managed by our CacheManager.
4 Get the configured Cache out of the CacheManager
You can also use the CachingProvider.getCacheManager() method that takes no arguments instead. The URI and ClassLoader used to configure the CacheManager will then use the vendor specific values returned by CachingProvider.getDefaultURI and .getDefaultClassLoader respectively.

Control JSR-107 MBeans from XML

When using Ehcache XML, you may want to enable management and / or statistics MBeans for JSR-107 caches. This is giving you control over the following:

  • javax.cache.configuration.CompleteConfiguration.isStatisticsEnabled

  • javax.cache.configuration.CompleteConfiguration.isManagementEnabled

You can do this at two different levels:

<config
    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xmlns='http://www.ehcache.org/v3'
    xmlns:jsr107='http://www.ehcache.org/v3/jsr107'
    xsi:schemaLocation="
        http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
        http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">


  <service>
    <jsr107:defaults enable-management="true" enable-statistics="true"/> (1)
  </service>

  <cache alias="stringCache"> (2)
    <key-type>java.lang.String</key-type>
    <value-type>java.lang.String</value-type>
    <heap unit="entries">2000</heap>
  </cache>

  <cache alias="overrideCache">
    <key-type>java.lang.String</key-type>
    <value-type>java.lang.String</value-type>
    <heap unit="entries">2000</heap>
    <jsr107:mbeans enable-management="false" enable-statistics="false"/> (3)
  </cache>

  <cache alias="overrideOneCache">
    <key-type>java.lang.String</key-type>
    <value-type>java.lang.String</value-type>
    <heap unit="entries">2000</heap>
    <jsr107:mbeans enable-statistics="false"/> (4)
  </cache>
</config>
1 Using the JSR-107 service extension, you can enable MBeans by default
2 The cache stringCache will have both MBeans enabled, according to the service configuration
3 The cache overrideCache will have both MBeans disabled, overriding the service configuration
4 The cache overrideOneCache will have the statistics MBean disabled, while the management one remains enabled according to the service configuration

Supplement JSR-107’s configurations

You can also create cache-templates, see the Cache Templates section of the XML Documentation for more details. The Ehcache 3 JSR-107 Caching Provider comes with an extension to the regular XML configuration so you can:

  1. Configure a default template from which all programmatically created Cache instances inherit, and

  2. Configure a given named Cache to inherit from a specific template.

This feature is particularly useful to configure Cache beyond the JSR-107 specification, for example, giving Cache a capacity constraint. All that’s needed is adding a jsr107 service in your XML configuration file:

<config
    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xmlns='http://www.ehcache.org/v3'
    xmlns:jsr107='http://www.ehcache.org/v3/jsr107'
    xsi:schemaLocation="
        http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
        http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd"> (1)

  <service> (2)
    <jsr107:defaults default-template="tinyCache"> (3)
      <jsr107:cache name="foos" template="clientCache"/> (4)
      <jsr107:cache name="byRefCache" template="byRefTemplate"/>
      <jsr107:cache name="byValCache" template="byValueTemplate"/>
      <jsr107:cache name="weirdCache1" template="mixedTemplate1"/>
      <jsr107:cache name="weirdCache2" template="mixedTemplate2"/>
    </jsr107:defaults>
  </service>

  <cache-template name="clientCache">
    <key-type>java.lang.String</key-type>
    <value-type>com.pany.domain.Client</value-type>
    <expiry>
      <ttl unit="minutes">2</ttl>
    </expiry>
    <heap unit="entries">2000</heap>
  </cache-template>

  <cache-template name="tinyCache">
    <heap unit="entries">20</heap>
  </cache-template>

  <cache-template name="byRefTemplate">
    <key-type copier="org.ehcache.impl.copy.IdentityCopier">java.lang.Long</key-type>
    <value-type copier="org.ehcache.impl.copy.IdentityCopier">com.pany.domain.Client</value-type>
    <heap unit="entries">10</heap>
  </cache-template>

  <cache-template name="byValueTemplate">
    <key-type copier="org.ehcache.impl.copy.SerializingCopier">java.lang.Long</key-type>
    <value-type copier="org.ehcache.impl.copy.SerializingCopier">com.pany.domain.Client</value-type>
    <heap unit="entries">10</heap>
  </cache-template>

  <cache-template name="mixedTemplate1">
    <key-type copier="org.ehcache.impl.copy.IdentityCopier">java.lang.Long</key-type>
    <value-type copier="org.ehcache.impl.copy.SerializingCopier">com.pany.domain.Client</value-type>
    <heap unit="entries">10</heap>
  </cache-template>

  <cache-template name="mixedTemplate2">
    <key-type copier="org.ehcache.impl.copy.SerializingCopier">java.lang.Long</key-type>
    <value-type copier="org.ehcache.impl.copy.IdentityCopier">com.pany.domain.Client</value-type>
    <heap unit="entries">10</heap>
  </cache-template>
</config>
1 First, declare a namespace for the 107 extension, e.g. jsr107
2 Within a service element at the top of you configuration, add a jsr107:defaults element
3 The element takes an optional attribute default-template, which references the cache-template to use for all javax.cache.Cache created by the application at runtime using javax.cache.CacheManager.createCache. In this example, the default cache-template used will be tinyCache, meaning that atop of their particular config, programmatically created Cache instances will have their capacity constrained to 20 entries.
4 Nested within the jsr107:defaults, add specific cache-templates to use for given named Cache, e.g. when creating the Cache named foos at runtime, Ehcache will enhance its config, giving it a capacity of 2000 entries, as well as insuring both key and value types are String.
See this xsd for complete definition

Using the above configuration, you can not only supplement but also override the configuration of JSR-107 created caches without modifying the application code.

MutableConfiguration<Long, Client> mutableConfiguration = new MutableConfiguration<Long, Client>();
mutableConfiguration.setTypes(Long.class, Client.class); (1)

Cache<Long, Client> anyCache = manager.createCache("anyCache", mutableConfiguration); (2)

CacheRuntimeConfiguration<Long, Client> ehcacheConfig = (CacheRuntimeConfiguration<Long, Client>)anyCache.getConfiguration(
    Eh107Configuration.class).unwrap(CacheRuntimeConfiguration.class); (3)
ehcacheConfig.getResourcePools().getPoolForResource(ResourceType.Core.HEAP).getSize(); (4)

Cache<Long, Client> anotherCache = manager.createCache("byRefCache", mutableConfiguration);
assertFalse(anotherCache.getConfiguration(Configuration.class).isStoreByValue()); (5)

MutableConfiguration<String, Client> otherConfiguration = new MutableConfiguration<String, Client>();
otherConfiguration.setTypes(String.class, Client.class);
otherConfiguration.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE)); (6)

Cache<String, Client> foosCache = manager.createCache("foos", otherConfiguration);(7)
CacheRuntimeConfiguration<Long, Client> foosEhcacheConfig = (CacheRuntimeConfiguration<Long, Client>)foosCache.getConfiguration(
    Eh107Configuration.class).unwrap(CacheRuntimeConfiguration.class);
Client client1 = new Client("client1", 1);
foosEhcacheConfig.getExpiry().getExpiryForCreation(42L, client1).getLength(); (8)

CompleteConfiguration<String, String> foosConfig = foosCache.getConfiguration(CompleteConfiguration.class);

try {
  final Factory<ExpiryPolicy> expiryPolicyFactory = foosConfig.getExpiryPolicyFactory();
  ExpiryPolicy expiryPolicy = expiryPolicyFactory.create(); (9)
  throw new AssertionError("Expected UnsupportedOperationException");
} catch (UnsupportedOperationException e) {
  // Expected
}
1 Assume existing JSR-107 configuration code, which is store-by-value by default
2 that creates JSR-107 Cache
3 If you were to get to the Ehcache RuntimeConfiguration
4 you could verify that the template configured capacity is applied to the cache and returns 20 here.
5 The cache template will override the JSR-107 cache’s store-by-value config to store-by-ref since the byRefTemplate template that is used to create the cache is configured explicitly using IdentityCopier.
6 Templates will also override JSR-107 config, see here a configuration with TTL 1 minute
7 used to create a cache where the template says TTL 2 minutes.
8 And we can indeed verify that the template provided configuration has been applied, duration will be 2 minutes and not 1.
9 One drawback of this is that when getting at the CompleteConfiguration, you no longer have access to the factories from JSR-107.
As mentioned in step 5, in order to override store-by-value configuration of a JSR-107 cache using templates you can explicitly configure the template using IdentityCopier. But the usage of IdentityCopier is not mandatory to get a store-by-ref cache. You can use any custom copier implementation that does not perform any "copying" but returns the exact same reference that gets passed into the copy methods. IdentityCopier is just one that we have provided to make your life easier.