Terracotta Ehcache 10.2 | Ehcache API Developer Guide | The JCache (JSR-107) Cache Provider | Integrating JCache and Ehcache Configurations
 
Integrating JCache and Ehcache Configurations
As mentioned already, JCache offers a minimal set of configuration that is ideal for an in-memory cache. But Ehcache native APIs support topologies that are much more complex and provide more features. At times, application developers might want to configure caches that are much complex (in terms of topology or features) than the ones that the JCache MutableConfiguration permits, and still be able to use JCache's caching APIs. Ehcache provides several ways to achieve this, as described in the following sections.
Accessing the underlying Ehcache configuration from a JCache configuration
When you create a Cache on a CacheManager using a MutableConfiguration - in other words, using only JCache types - you can still get to the underlying Ehcache CacheRuntimeConfiguration:

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 JCache cache using the MutableConfiguration interface from the JCache specification.
2
Get to the JCache CompleteConfiguration.
3
Get to the configuration bridge connecting Ehcache and JCache.
4
Unwrap to the Ehcache CacheRuntimeConfiguration type.
CacheManager level configuration
If you need to configure features at the CacheManager level, like the persistence directory, you will have to use provider specific APIs.
The way you do this is as follows:
CachingProvider cachingProvider = Caching.getCachingProvider();
EhcacheCachingProvider ehcacheProvider =
(EhcacheCachingProvider) cachingProvider; // 1

DefaultConfiguration configuration =
new DefaultConfiguration(ehcacheProvider.getDefaultClassLoader(),
new DefaultPersistenceConfiguration(getPersistenceDirectory())); // 2

CacheManager cacheManager = ehcacheProvider.getCacheManager(
ehcacheProvider.getDefaultURI(), configuration); // 3
1
Cast the CachingProvider into the Ehcache specific implementation org.ehcache.jsr107.EhcacheCachingProvider,
2
Create a configuration using the specific Ehcache DefaultConfiguration and pass it some CacheManager level configurations,
3
Create the CacheManager using the method that takes an Ehcache configuration as a parameter.
Cache level configuration
You can also create a JCache Cache using an Ehcache CacheConfiguration. When using this mechanism, no JCache 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. You can use a builder as shown here, or alternatively use an XML configuration (as described in the following section).
2
Get a JCache configuration by wrapping the Ehcache configuration.
3
Get back to the Ehcache CacheConfiguration.
4
... or even to the runtime configuration.
5
No JCache CompleteConfiguration is available in this context.
Building the JCache configuration using an Ehcache XML configuration
Another way to have the full Ehcache configuration options on your JCache caches while having no code dependency on Ehcache as the cache provider is to use XML-based configuration. See Configuring a CacheManager Using XML for more details on configuring caches in XML.
The following is an example of an XML configuration:
<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>
Here is an example of how to access the XML configuration using JCache:

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
Invoke javax.cache.spi.CachingProvider.getCacheManager(java.net.URI, java.lang.ClassLoader)
2
... and pass in a URI that resolves to an Ehcache XML configuration file.
3
The second argument is 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.
Note: You can alternatively use the CachingProvider.getCacheManager() method that takes no arguments. The URI and ClassLoader used to configure the CacheManager will then use the vendor specific values returned by CachingProvider.getDefaultURI and .getDefaultClassLoader respectively. Be aware that these are not entirely specified for Ehcache currently and may change in future releases!
Enabling/Disabling MBeans for JCache using an Ehcache XML configuration
When using an Ehcache XML configuration, you may want to enable management and / or statistics MBeans for JCache caches. This gives 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 JCache 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, whereas the management MBean will be enabled according to the service configuration.
Supplementing JCache cache configurations using Ehcache XML extensions
You can also create cache-templates. See the Cache Templates topic of the section The XML Schema Definition for more details. Ehcache as a caching provider for JCache 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 scope of the JCache specification, for example, giving Cache a capacity constraint. To do this, add 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 JCache extension, e.g. jsr107.
2
Within a service element at the top of your 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 elements created by the application at runtime using javax.cache.CacheManager.createCache. In this example, the default cache-template used will be tinyCache, meaning that in addition to their particular configuration, any programmatically created Cache instances will have their capacity constrained to 20 entries.
4
Nested within the jsr107:defaults element, add specific cache-templates to use for the given named Cache. So, for example, when creating the Cache named foos at runtime, Ehcache will enhance its configuration, giving it a capacity of 2000 entries, as well as ensuring that both key and value types are String.
Note: The XSD schema definitions that describe the syntax used for the Ehcache XML configuration are referenced at XSD namespaces and locations.
Using the above configuration, you can not only supplement but also override the configuration of JCache-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 JCache configuration code, which is store-by-value by default
2
... that creates JCache 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 JCache cache's store-by-value configuration to store-by-reference, since the byRefTemplatetemplate that is used to create the cache is configured explicitly using IdentityCopier.
6
Templates will also override the JCache configuration, in this case using a configuration with Time to Live (TTL) 1 minute.
7
Create a cache where the template sets the TTL to 2 minutes.
8
And we can indeed verify that the configuration provided in the template has been applied; the duration will be 2 minutes and not 1 minute.
9
One drawback of this is that when getting at the CompleteConfiguration, you no longer have access to the factories from JCache.
Note: As mentioned in step 5, in order to override the store-by-value configuration of a JCache cache using templates, you can explicitly configure the template using IdentityCopier. But the usage of IdentityCopier is not mandatory to get a store-by-reference 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 an example that we have provided for your convenience.

Copyright © 2010-2019 | Software AG, Darmstadt, Germany and/or Software AG USA, Inc., Reston, VA, USA, and/or its subsidiaries and/or its affiliates and/or their licensors.
Innovation Release