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
|
| 4 | Get the configured
Cache out of the
CacheManager.
|
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:
- Configure a default template from which all programmatically
created
Cacheinstances inherit, and - Configure a given named
Cacheto 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.
|
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<>();
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<>();
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).toMinutes(); // <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.
|
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.