EhcacheJekyll2023-05-15T16:55:27+00:00https://www.ehcache.org/Ehcachehttps://www.ehcache.org/tc-oss@softwareag.comhttps://www.ehcache.org/blog/2017/03/15/spontaneous-cache-creation2017-03-15T16:00:00+00:002017-03-15T16:00:00+00:00Henri Tremblayhttps://www.ehcache.org<div class="paragraph">
<p>I played a lot with JCache connectors lately. To plug <a href="http://www.ehcache.org">Ehcache 3</a> to different things.</p>
</div>
<div class="paragraph">
<p>I noticed one really dangerous thing. Frameworks tend to spontaneously create caches that were not explicitly defined. I
think it is coming from the JCache spirit that there should be a default cache configuration. It is nonetheless a bad idea.</p>
</div>
<div class="paragraph">
<p>Let’s look at two examples</p>
</div>
<div class="sect1">
<h2 id="spring"><a class="anchor" href="#spring"></a>Spring</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Caching in Spring is implemented by <code>spring-cache</code>. To plug JCache to <code>spring-cache</code> you use the <code>JCacheCacheManager</code>. By default
when a cache isn’t available in the <code>CacheManager</code>, Spring calls <code>JCacheCacheManager.getMissingCache</code>. So far so good.</p>
</div>
<div class="paragraph">
<p>The default implementation for this method returns <code>null</code> when a cache doesn’t exist. This <code>null</code> will then be handled at
higher levels to throw a nice exception.</p>
</div>
<div class="paragraph">
<p>If you want to explicitly support spontaneous cache creation, <code>getMissingCache</code> is where you should put your creation code.</p>
</div>
<div class="paragraph">
<p>However, watch out if you do that. You might lose track of all the existing caches. And please, <strong>never</strong> do the following.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">@Override
protected Cache getMissingCache(String name) {
Cache cache = super.getMissingCache(name);
if(cache == null) {
return new JCacheCache(cacheManager.createCache(name, new MutableConfiguration<>()));
}
return cache;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>It returns a cache configured using default. It is <strong>never</strong> what you want. I will repeat that because you might think I don’t
mean it. You might think defaults are cool. <strong>It is never what you want</strong>.</p>
</div>
<div class="paragraph">
<p>Then, as usual, Spring tries to be nice with us. So if you enable caching (<code>@EnableCaching</code>), that the JSR-107 API is in
the classpath and that you do not expose any <code>CacheManager</code>, Spring will create one for you.</p>
</div>
<div class="paragraph">
<p>The <code>JCacheCacheConfiguration</code> will get a default JSR-107 <code>CacheManager</code> and add a list of caches taken from the cache property
<code>spring.cache.cache-names</code>. These caches are by default created using a <code>new MutableConfiguration<>()</code>. As we said above, this
is not a correctly configured cache.</p>
</div>
<div class="paragraph">
<p>The solution is to expose the wanted cache configuration in a bean.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">@Bean
public javax.cache.configuration.Configuration<Object, Object> cacheConfiguration() {
CacheConfiguration<Object, Object> cacheConfiguration = CacheConfigurationBuilder
.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder
.newResourcePoolsBuilder().heap(100))
.build();
javax.cache.configuration.Configuration<Object, Object> configuration =
Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration);
return configuration;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This bean will be magically used as cache default. You should always do this and never let <code>`new MutableConfiguration<>()</code>
be used.</p>
</div>
<div class="paragraph">
<p>An alternative (preferred in fact, thanks <a href="https://twitter.com/snicoll">Stéphane Nicoll</a> for the tip), it to use a <code>JCacheManagerCustomizer</code> instead
of the <code>spring.cache.cache-names</code> property. Like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">@Bean
public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
Configuration<Object, Object> cacheConfiguration = createCacheConfiguration();
cm.createCache("vets", cacheConfiguration);
};
}
private Configuration<Object, Object> createCacheConfiguration() {
return Eh107Configuration.fromEhcacheCacheConfiguration(CacheConfigurationBuilder
.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder
.newResourcePoolsBuilder().heap(100)));
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="hibernate"><a class="anchor" href="#hibernate"></a>Hibernate</h2>
<div class="sectionbody">
<div class="paragraph">
<p>To use JCache with Hibernate we need to use the <code>JCacheRegionFactory</code>. The problem with <code>JCacheRegionFactory</code> is that by
default if a cache is not found, it will spontaneously create a cache by passing <code>new MutableConfiguration()</code>. This means
that instead of using a properly configured cache, you end up with some random default configuration (infinite on heap for
Ehcache).</p>
</div>
<div class="paragraph">
<p>This is really bad because it is pretty hard to detect.</p>
</div>
<div class="paragraph">
<p>What I recommend in this case is, again, to override the default. In the latest Hibernate versions (5.2.8+) (thanks to
<a href="https://github.com/hibernate/hibernate-orm/pull/1783">HHH-1783</a>, we can do the following</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">@Override
protected Cache<Object, Object> createCache(String regionName, Properties properties, CacheDataDescription metadata) {
throw new IllegalArgumentException("Unknown hibernate cache: " + regionName);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In older versions, sadly, there is no straightforward cache creation method to override. The best we have is
<code>newDefaultConfig</code> which provides the default configuration. One sad thing is that you don’t have the actual cache name
here. You will need to debug to know it.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">@Override
protected Configuration<Object, Object> newDefaultConfig(Properties properties, CacheDataDescription metadata) {
throw new IllegalArgumentException("Unknown hibernate cache: " + metadata);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Again, an alternative solution would be to provide a meaningful default cache configuration in this method.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion"><a class="anchor" href="#conclusion"></a>Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I do understand that frameworks do not like to fail with exceptions. This helps the feeling that they are working out of
the box.</p>
</div>
<div class="paragraph">
<p>But I still think silently not caching or providing random default configuration is dangerous. Using my two workarounds
should prevent a lot of headaches.</p>
</div>
</div>
</div>
<p><a href="https://www.ehcache.org/blog/2017/03/15/spontaneous-cache-creation.html">Prevent spontaneous cache creation</a> was originally published by Henri Tremblay at <a href="https://www.ehcache.org">Ehcache</a> on March 15, 2017.</p>https://www.ehcache.org/resources/2016/06/23/ehcache-out-element2016-06-23T00:00:00+00:002016-06-23T00:00:00+00:00https://www.ehcache.org<div class="ulist">
<ul>
<li>
<p>Conference page: <a href="http://cfp.devoxx.pl/2016/talk/DIW-5033/Ehcache%20out%20of%20his%20Element" class="bare">http://cfp.devoxx.pl/2016/talk/DIW-5033/Ehcache%20out%20of%20his%20Element</a></p>
</li>
<li>
<p>Video: <a href="https://www.youtube.com/watch?v=RSpgLZ1U75g" class="bare">https://www.youtube.com/watch?v=RSpgLZ1U75g</a></p>
</li>
</ul>
</div>
<p><a href="https://www.ehcache.org/resources/2016/06/23/ehcache-out-element.html">Ehcache out of his Element</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on June 23, 2016.</p>https://www.ehcache.org/resources/2016/06/10/caching-1012016-06-10T00:00:00+00:002016-06-10T00:00:00+00:00https://www.ehcache.org<div class="paragraph">
<p>This session, while similar to the previous one with the same title, has been improved based on feedback and experience.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Conference page: <a href="https://cfp.devoxx.co.uk/2016/talk/FWR-0829/Caching_101:_Caching_on_the_JVM_(and_beyond" class="bare">https://cfp.devoxx.co.uk/2016/talk/FWR-0829/Caching_101:_Caching_on_the_JVM_(and_beyond</a>)</p>
</li>
<li>
<p>Video: <a href="https://www.youtube.com/watch?v=FQfd8x29Ud8" class="bare">https://www.youtube.com/watch?v=FQfd8x29Ud8</a></p>
</li>
</ul>
</div>
<p><a href="https://www.ehcache.org/resources/2016/06/10/caching-101.html">Caching 101: Caching on the JVM (and beyond)</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on June 10, 2016.</p>https://www.ehcache.org/blog/2016/05/18/ehcache3_jsr107_spring2016-05-18T05:30:00+00:002016-05-18T05:30:00+00:00Geoff Gibsonhttps://www.ehcache.org<div class="paragraph">
<p>In this post I would like to demonstrate how to use <a href="https://github.com/ehcache/ehcache3">Ehcache 3.0</a> as Spring’s caching implementation.
This article will show you how to use <a href="https://github.com/ehcache/ehcache3">Ehcache 3.0</a>, <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/">Spring 4.2</a> and the <a href="https://jcp.org/en/jsr/detail?id=107">JSR-107</a> annotations to build a simple Spring web application.
I have also included some background reading for those of you that would like to read more in depth on the subject matter.</p>
</div>
<div class="sect1">
<h2 id="background-reading"><a class="anchor" href="#background-reading"></a>Background reading</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>Ehcache 3.0 Documentation</p>
<div class="ulist">
<ul>
<li>
<p><a href="http://www.ehcache.org/documentation/3.0/" class="bare">http://www.ehcache.org/documentation/3.0/</a></p>
</li>
</ul>
</div>
</li>
<li>
<p>Spring Cache Abstraction</p>
<div class="ulist">
<ul>
<li>
<p><a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html" class="bare">http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html</a></p>
</li>
</ul>
</div>
</li>
<li>
<p>Spring Cache Abstraction, JCache (JSR-107) annotations</p>
<div class="ulist">
<ul>
<li>
<p><a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html#cache-jsr-107" class="bare">http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html#cache-jsr-107</a></p>
</li>
<li>
<p><a href="https://spring.io/blog/2014/04/14/cache-abstraction-jcache-jsr-107-annotations-support" class="bare">https://spring.io/blog/2014/04/14/cache-abstraction-jcache-jsr-107-annotations-support</a></p>
</li>
</ul>
</div>
</li>
<li>
<p>The following is the GitHub example associated with this post:</p>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/gibsong/ehcache-jsr107-spring" class="bare">https://github.com/gibsong/ehcache-jsr107-spring</a></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="jsr-107jcache-annotations"><a class="anchor" href="#jsr-107jcache-annotations"></a>JSR-107(JCache) Annotations</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you are familiar with Spring, you know that it provides annotations to assist in developing applications.
In regards to caching, Spring offers support for two sets of annotations that can be used to implement caching.
You have the original Spring annotations and the new JSR-107 annotations.
The original Spring annotations are available to use with Spring versions 3.1+, while the JSR-107 annotations are only available in Spring 4.1+.
In this example we are going to use the JSR-107 annotations.
Below I have listed the most commonly used JSR-107(JCache) annotations, with brief descriptions as well as links to their API’s.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>@CacheDefaults</strong> - allows configuration of defaults at the class level.
For instance, you can define a cache name at the class level and this will be used as the default.</p>
<div class="ulist">
<ul>
<li>
<p><a href="http://static.javadoc.io/javax.cache/cache-api/1.0.0/javax/cache/annotation/CacheDefaults.html" class="bare">http://static.javadoc.io/javax.cache/cache-api/1.0.0/javax/cache/annotation/CacheDefaults.html</a></p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>@CacheResult</strong> - Cache the return value of the method.
The first time the method is invoked with a particular key it will be run and the value will be cached.
On subsequent calls with the same key if the value is still cached it will be taken from the cache instead of running the method.</p>
<div class="ulist">
<ul>
<li>
<p><a href="http://static.javadoc.io/javax.cache/cache-api/1.0.0/javax/cache/annotation/CacheResult.html" class="bare">http://static.javadoc.io/javax.cache/cache-api/1.0.0/javax/cache/annotation/CacheResult.html</a></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
Remember that the value is not cached forever.
The length of time will be influenced by how you set your eviction policy, TTL and TTI.
Please see this discussion for the difference between TTL and TTI: <a href="http://stackoverflow.com/questions/2583429/how-to-differentiate-between-time-to-live-and-time-to-idle-in-ehcache" class="bare">http://stackoverflow.com/questions/2583429/how-to-differentiate-between-time-to-live-and-time-to-idle-in-ehcache</a>
</td>
</tr>
</table>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>@CachePut</strong> - Cache the value specified as the @CacheValue.
This is similar to @CacheResult but the difference is it will cache the @CacheValue every time the method is called.</p>
<div class="ulist">
<ul>
<li>
<p><a href="http://static.javadoc.io/javax.cache/cache-api/1.0.0/javax/cache/annotation/CachePut.html" class="bare">http://static.javadoc.io/javax.cache/cache-api/1.0.0/javax/cache/annotation/CachePut.html</a></p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>@CacheRemove</strong> - removes entries from the specified cache that match the provided/generated key</p>
<div class="ulist">
<ul>
<li>
<p><a href="http://static.javadoc.io/javax.cache/cache-api/1.0.0/javax/cache/annotation/CacheRemove.html" class="bare">http://static.javadoc.io/javax.cache/cache-api/1.0.0/javax/cache/annotation/CacheRemove.html</a></p>
</li>
</ul>
</div>
</li>
<li>
<p><strong>@CacheRemoveAll</strong> - removes all elements in the specified cache</p>
<div class="ulist">
<ul>
<li>
<p><a href="http://static.javadoc.io/javax.cache/cache-api/1.0.0/javax/cache/annotation/CacheRemoveAll.html" class="bare">http://static.javadoc.io/javax.cache/cache-api/1.0.0/javax/cache/annotation/CacheRemoveAll.html</a></p>
</li>
</ul>
</div>
</li>
<li>
<p>The <strong>javax.cache.annotation</strong> package which includes all the JSR-107 annotations:</p>
<div class="ulist">
<ul>
<li>
<p><a href="http://www.javadoc.io/doc/javax.cache/cache-api/1.0.0" class="bare">http://www.javadoc.io/doc/javax.cache/cache-api/1.0.0</a></p>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="steps"><a class="anchor" href="#steps"></a>Steps</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Let’s get started!
Below are a few quick steps to get you up and running with Ehcache 3, Spring 4.1+ and JSR-107.
The entire example can be found on GitHub at <a href="https://github.com/gibsong/ehcache-jsr107-spring" class="bare">https://github.com/gibsong/ehcache-jsr107-spring</a></p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Create a maven Spring project.
Maven 3.2 or greater is required.</p>
</li>
<li>
<p>Add Ehcache 3 to your pom.xml.</p>
<div class="listingblock">
<div class="title">pom.xml</div>
<div class="content">
<pre class="prettyprint highlight"><code data-lang="xml"> <dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.0.0</version> <i class="conum" data-value="1"></i><b>(1)</b>
</dependency></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Be sure to substitute the version number above with the version number of Ehcache that you want to use.</td>
</tr>
</table>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
The Ehcache 3 jar must be on the classpath!
Remove all existing caching provider jars from the classpath to ensure that the right implementation is used.
</td>
</tr>
</table>
</div>
</li>
<li>
<p>Add the jar for the JSR-107 API to the pom.xml</p>
<div class="listingblock">
<div class="title">pom.xml</div>
<div class="content">
<pre class="prettyprint highlight"><code data-lang="xml"> <dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency></code></pre>
</div>
</div>
</li>
<li>
<p>Add Spring boot jars</p>
<div class="listingblock">
<div class="title">pom.xml</div>
<div class="content">
<pre class="prettyprint highlight"><code data-lang="xml"> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <i class="conum" data-value="1"></i><b>(1)</b>
<version>1.3.2.RELEASE</version>
</parent>
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId> <i class="conum" data-value="2"></i><b>(2)</b>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <i class="conum" data-value="3"></i><b>(3)</b>
</dependency>
...
<dependencies></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Add the spring-boot-starter-parent parent project to use Spring boot</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Add the spring-boot-starter-cache to use Spring Framework’s caching support</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Add spring-boot-starter-web to use Spring MVC</td>
</tr>
</table>
</div>
</li>
<li>
<p>Set the <strong>spring.cache.jcache.config</strong> property to include the classpath and ehcache.xml file.
This needs to be done in the application.properties file.</p>
<div class="listingblock">
<div class="title">application.properties</div>
<div class="content">
<pre>spring.cache.jcache.config=classpath:ehcache.xml</pre>
</div>
</div>
</li>
<li>
<p>Enable caching.
This can be done in 1 of 2 ways:</p>
<div class="olist loweralpha">
<ol class="loweralpha" type="a">
<li>
<p>Enable with @EnableCaching annotation:</p>
<div class="listingblock">
<div class="title">SpringJsr107Ehcache3Application.java</div>
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java"> @EnableCaching
public class SpringJsr107Ehcache3Application
{
public static void main(String[] args)
{
SpringApplication.run(SpringJsr107Ehcache3Application.class, args);
}
}</code></pre>
</div>
</div>
</li>
<li>
<p>Or enable from the Spring xml configuration file by adding the following tag: <cache:annotation-driven /></p>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="xml"> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven />
</beans></code></pre>
</div>
</div>
</li>
</ol>
</div>
</li>
<li>
<p>Declare caching on a method by adding the @CacheResult annotation.</p>
<div class="listingblock">
<div class="title">PersonService.java</div>
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java"> @CacheResult <i class="conum" data-value="1"></i><b>(1)</b>
Person getPerson(int ssn)
{
switch (ssn)
{
case 123456789:
return new Person(ssn, "Geoff", "Gibson");
case 987654321:
return new Person(ssn, "Cory", "Beck");
default:
return new Person(ssn,"John","Doe");
}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Add the @CacheResult annotation above the method.
In this case the key is the "int ssn" parameter and the value cached is a Person instance.
So if you call this method with ssn="123456789", the Person(ssn, "Geoff", "Gibson") will be returned and cached.
The next time the getPerson(…​) method is called with ssn="123456789" (assuming the key/value wasn’t evicted from the cache) the method won’t run and instead it will grab Person(ssn, "Geoff", "Gibson") from the cache and return it.</td>
</tr>
</table>
</div>
</li>
<li>
<p>Configure ehcache.xml</p>
<div class="listingblock">
<div class="title">ehcache.xml</div>
<div class="content">
<pre class="prettyprint highlight"><code data-lang="xml"><config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3' <i class="conum" data-value="1"></i><b>(1)</b>
xmlns:jsr107='http://www.ehcache.org/v3/jsr107'> <i class="conum" data-value="2"></i><b>(2)</b>
<service>
<jsr107:defaults>
<jsr107:cache name="people" template="heap-cache"/> <i class="conum" data-value="3"></i><b>(3)</b>
</jsr107:defaults>
</service>
<cache-template name="heap-cache">
<listeners> <i class="conum" data-value="4"></i><b>(4)</b>
<listener>
<class>org.terracotta.ehcache.EventLogger</class>
<event-firing-mode>ASYNCHRONOUS</event-firing-mode>
<event-ordering-mode>UNORDERED</event-ordering-mode>
<events-to-fire-on>CREATED</events-to-fire-on> <i class="conum" data-value="5"></i><b>(5)</b>
<events-to-fire-on>UPDATED</events-to-fire-on> <i class="conum" data-value="6"></i><b>(6)</b>
<events-to-fire-on>EXPIRED</events-to-fire-on> <i class="conum" data-value="7"></i><b>(7)</b>
<events-to-fire-on>REMOVED</events-to-fire-on> <i class="conum" data-value="8"></i><b>(8)</b>
<events-to-fire-on>EVICTED</events-to-fire-on> <i class="conum" data-value="9"></i><b>(9)</b>
</listener>
</listeners>
<resources>
<heap unit="entries">2000</heap> <i class="conum" data-value="10"></i><b>(10)</b>
<offheap unit="MB">100</offheap> <i class="conum" data-value="11"></i><b>(11)</b>
</resources>
</cache-template>
</config></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The core namespace, the xsd can be found here: <a href="http://www.ehcache.org/schema/ehcache-core-3.0.xsd" class="bare">http://www.ehcache.org/schema/ehcache-core-3.0.xsd</a></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The JSR-107 namespace, the xsd can be found here: <a href="http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd" class="bare">http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd</a></td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Defines a cache with alias "people", which inherits from cache-template "heap-cache"</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>This section allows you to add cache event listeners.
I added a listener for 5 events.
Each event will be logged, by the EventLogger class, when it occurs.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Defines a CREATED event, when an entry is added to the cache, with this listener.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Defines an UPDATED event, when an entry is updated in the cache, with this listener.
However in this example this one will never be used.
I just added it as an example.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Defines an EXPIRED event, when an entry is expired from the cache, with this listener.</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>Defines an REMOVED event, when an entry is removed from the cache, with this listener.</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td>Defines an EVICTED event, when an entry is evicted from the cache, with this listener.</td>
</tr>
<tr>
<td><i class="conum" data-value="10"></i><b>10</b></td>
<td>The heap is configured to allow 2000 entries</td>
</tr>
<tr>
<td><i class="conum" data-value="11"></i><b>11</b></td>
<td>The offheap storage is configured with 100 MB of space.
Remember the unit of measure is case sensitive.</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
XML Configuration Documentation: <a href="http://www.ehcache.org/documentation/3.0/xml.html" class="bare">http://www.ehcache.org/documentation/3.0/xml.html</a>
</td>
</tr>
</table>
</div>
</li>
<li>
<p>Create a cache by implementing the JCacheManagerCustomizer.customize(CacheManager cacheManager) method, which will be invoked before the CacheManager is used.</p>
<div class="listingblock">
<div class="title">PersonService.java</div>
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java"> @Component
public static class CachingSetup implements JCacheManagerCustomizer
{
@Override
public void customize(CacheManager cacheManager)
{
cacheManager.createCache("people", new MutableConfiguration<>() <i class="conum" data-value="1"></i><b>(1)</b>
.setExpiryPolicyFactory(TouchedExpiryPolicy.factoryOf(new Duration(SECONDS, 10))) <i class="conum" data-value="2"></i><b>(2)</b>
.setStoreByValue(false)
.setStatisticsEnabled(true));
}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Creates a cache with an alias of "people".</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This line sets the expiration policy.
In this case we set it to 10 seconds.
Thus, if an entry hasn’t been touched (created, updated, or accessed) for the last 10 seconds it will be evicted.</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
JCacheManagerCustomizer API: <a href="https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/cache/JCacheManagerCustomizer.html" class="bare">https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/cache/JCacheManagerCustomizer.html</a>
</td>
</tr>
</table>
</div>
</li>
<li>
<p>Now you can build the project by running the following maven command: mvn clean install</p>
</li>
<li>
<p>To run the application use this maven command: mvn spring-boot:run</p>
</li>
<li>
<p>To make a get request to the application use the following url: <a href="http://localhost:8080/person/{ssn}" class="bare">http://localhost:8080/person/{ssn}</a>
IMPORTANT: Remember to replace {ssn} in the url with an integer value.
123456789 and 987654321 are mapped to unique Person instances, while anything else maps to a generic Person instance.</p>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion"><a class="anchor" href="#conclusion"></a>Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Wow wasn’t that easy!
I hope you enjoyed my quick start tutorial on how to use Ehcache 3 with Spring 4.1+ and JSR-107.
If you have any questions please feel free to send them to me at <a href="mailto:geoff.gibson@softwareag.com">geoff.gibson@softwareag.com</a> and I will be happy to help out.</p>
</div>
</div>
</div>
<p><a href="https://www.ehcache.org/blog/2016/05/18/ehcache3_jsr107_spring.html">Ehcache 3, JSR-107 and Spring 4.2</a> was originally published by Geoff Gibson at <a href="https://www.ehcache.org">Ehcache</a> on May 18, 2016.</p>https://www.ehcache.org/blog/2016/05/12/ehcache3-serializers2016-05-12T15:45:00+00:002016-05-12T15:45:00+00:00Albin Sureshhttps://www.ehcache.org<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>The Ehcache 3 documentation at <a href="http://www.ehcache.org/documentation/3.0/serializers-copiers.html#serializers">Serializers</a>
gives you an overview of how to use custom serializers with a cache.
The section on <a href="http://www.ehcache.org/documentation/3.0/serializers-copiers.html#persistent-vs-transient-caches">Persistent and Transient Serializers</a>
briefly covers the serializer contracts that must be honored while writing custom serializers to be used with
persistent/transient caches.</p>
</div>
<div class="paragraph">
<p>This article explains how you can write a transient/persistent custom serializer that works with Ehcache.
Here we discuss the significance of transient serializers and persistent serializers in detail through some
practical examples.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="serializer-types"><a class="anchor" href="#serializer-types"></a>Serializer types</h2>
<div class="sectionbody">
<div class="paragraph">
<p>As indicated in the Ehcache documentation, serializers require a single argument constructor or a double-argument
constructor or both based on the type of cache they are used in.
The single-argument constructor is fit to be used with transient caches and the ones with the double-argument constructor can be used with persistent caches.
An implementation having both the constructors can be used with both <em>persistent</em> and <em>transient</em> caches.</p>
</div>
<div class="paragraph">
<p>Hmm…​ So what does that really mean?</p>
</div>
<div class="paragraph">
<p>If you look at the custom serializer implementations in the GettingStarted samples they are all have both the constructors
and if you look at the code they don’t do anything different.
It’s all standard java serialization.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>So what difference do the constructors make?</p>
</li>
<li>
<p>What is a <em>transient</em> serializer with single-argument constructor?</p>
</li>
<li>
<p>How do I implement a <em>persistent</em> serializer with the double-argument constructor?</p>
</li>
<li>
<p>When would I use both?</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Read along for the answers to these questions.</p>
</div>
<div class="paragraph">
<p>These constructors are associated with the state of the serializer implementations.
So if your custom serializer doesn’t have any state associated with it; that affects the serialization and
deserialization logic; then that is a serializer implementation that can safely be used with transient and persistent
caches. Such serializers would have both the constructors.
If you look at the <code>LongSerializer</code> or <code>StringSerializer</code> implementations in the GettingStarted samples, they don’t have
any state that the serialization and deserialization depend on.</p>
</div>
<div class="paragraph">
<p>So what are these serializers with <strong>state</strong>? I’ll try to explain that with some examples in the subsequent sections.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The code samples in this article were compiled and tested with Ehcache v3.0.0.
Complete samples can be found at <a href="https://github.com/albinsuresh/ehcache-demo" class="bare">https://github.com/albinsuresh/ehcache-demo</a>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="stateful-serializers"><a class="anchor" href="#stateful-serializers"></a>Stateful serializers</h3>
<div class="paragraph">
<p>I have an application that deals with fruits. So I have a <em>fruits</em> cache <code>Cache<Long, String></code> that holds the mappings
from fruit ids to fruit names.
If this cache is a multi-tiered one then the keys and values will be stored in their serialized form in the
non-heap tiers.
For simplicity I’ll restrict the scope of our discussion only to the values that are fruit names of type <code>String</code>.
I can use standard Java serialization to serialize these values.
But for some reason I wanted to reduce the amount of serialized data.
So instead of serializing the strings directly I decided to map all the fruit names to some integer and store those
serialized integers instead of strings thinking that it’d save some space(dumb, huh?).
Since this serializer is designed specifically for the fruits cache, I was fairly confident that the integer range would
be more than enough to handle all possible fruit names on this planet.
And here is the serializer implementation that I came up with:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">public class SimpleTransientStringSerializer implements Serializer<String> {
protected Map<Integer, String> idStringMap = new HashMap<Integer, String>();
protected Map<String, Integer> stringIdMap = new HashMap<String, Integer>();
protected int id = 0;
public SimpleTransientStringSerializer(ClassLoader loader) {
//no-op
}
@Override
public ByteBuffer serialize(final String object) throws SerializerException {
Integer currentId = stringIdMap.get(object);
if(currentId == null) {
stringIdMap.put(object, id);
idStringMap.put(id, object);
currentId = id++;
}
ByteBuffer buff = ByteBuffer.allocate(4);
buff.putInt(currentId).flip();
return buff;
}
@Override
public String read(final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
Integer mapping = binary.getInt();
String obj = idStringMap.get(mapping);
if(obj == null) {
throw new SerializerException("Unable to serialize: " + binary.array() + ". No value mapping found for " + mapping);
}
return obj;
}
@Override
public boolean equals(final String object, final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
return object.equals(read(binary));
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In short this is what the above serializer does: Whenever it gets a string(the fruit name, in our application) to be
serialized it checks if there is a mapping that exists already for that name in <code>stringIdMap</code>.
If yes, the mapped integer is retrieved from the map and that integer value is serialized.
If a mapping is not found, we generate a new <code>id</code> for the new fruit name add it to both the maps that we preserve
(<code>stringIdMap</code> and <code>idStringMap</code>) and then serialize this newly generated id.
Now on deserialization, the same <code>idStringMap</code> map is used to retrieve the fruit names from the deserialised integer
values.</p>
</div>
<div class="paragraph">
<p>So in the above serializer, the <code>idStringMap</code>, <code>stringIdMap</code> and the <code>id</code> constitutes the <em>state</em> of the serializer.
The serialization and deserialization depends on this state and would not work properly without that state.
This serializer has the single-argument constructor making it fit to be used with transient caches.
So now that we have a state-full serializer understanding the idea of <em>transient</em> and <em>persistent</em> serializers would be
simpler.</p>
</div>
<div class="paragraph">
<p>Here is a sample code that uses the <code>SimpleTransientStringSerializer</code> with a cache:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);
CacheConfiguration<Long, String> cacheConfig =
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, String.class, ResourcePoolsBuilder.heap(10).offheap(5, MemoryUnit.MB)) <i class="conum" data-value="1"></i><b>(1)</b>
.withValueSerializer(SimpleTransientStringSerializer.class) <i class="conum" data-value="2"></i><b>(2)</b>
.build();
Cache<Long, String> fruitsCache = cacheManager.createCache("fruitsCache", cacheConfig);
fruitsCache.put(1L, "apple");
fruitsCache.put(2L, "orange");
fruitsCache.put(3L, "mango");
assertThat(fruitsCache.get(1L), is("apple")); <i class="conum" data-value="3"></i><b>(3)</b>
assertThat(fruitsCache.get(3L), is("mango"));
assertThat(fruitsCache.get(2L), is("orange"));
assertThat(fruitsCache.get(1L), is("apple"));</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Create a multi-tiered cache that requires key and value serialization.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Configure a serializer for the values. The <code>SimpleTransientStringSerializer</code> in this case. For the sake of simplicity
we have omitted key serializer. Since one is not provided explicitly, ehcache would provide default serializers to
perform the key serialization.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Verify that the cache/serializer works.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In the previous section we demonstrated the use of a transient serializer.
We used that serializer with a transient cache and everything works just fine.
Now imagine what would happen if we use the same serializer with a persistent cache.
Everything would work as long as your application is running. Once you close the cache manager or end the application
the data associated with the cache will be persisted so that the same data will be available on a restart.
But there is a serious problem. The following piece of code would demonstrate that:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">CacheConfiguration<Long, String> cacheConfig =
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, String.class,
ResourcePoolsBuilder.heap(10).disk(10, MemoryUnit.MB, true)) <i class="conum" data-value="1"></i><b>(1)</b>
.withValueSerializer(SimpleTransientStringSerializer.class)
.build();
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(new CacheManagerPersistenceConfiguration(new File(PERSISTENCE_PATH))) <i class="conum" data-value="2"></i><b>(2)</b>
.withCache("fruitsCache", cacheConfig)
.build(true);
Cache<Long, String> fruitsCache = cacheManager.getCache("fruitsCache", Long.class, String.class); <i class="conum" data-value="3"></i><b>(3)</b>
fruitsCache.put(1L, "apple");
fruitsCache.put(2L, "mango");
fruitsCache.put(3L, "orange"); <i class="conum" data-value="4"></i><b>(4)</b>
assertThat(fruitsCache.get(1L), is("apple")); <i class="conum" data-value="5"></i><b>(5)</b>
cacheManager.close(); <i class="conum" data-value="6"></i><b>(6)</b>
cacheManager.init(); <i class="conum" data-value="7"></i><b>(7)</b>
fruitsCache = cacheManager.getCache("fruitsCache", Long.class, String.class); <i class="conum" data-value="8"></i><b>(8)</b>
assertThat(fruitsCache.get(1L), is("apple")); <i class="conum" data-value="9"></i><b>(9)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Create a cache configuration with persistent disk tier.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Configure the <code>LocalPersistenceService</code> for the cache manager.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Retrieve the cache.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Populate data.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Verify that everything works.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Close the cache manager.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Reinitialize the cache manager.</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>Retrieve the cache.</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td>Retrieve a cached/persisted value.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The above piece of code would fail in the cache creation step since the serializer provided does not meet the 2-arg
constructor requirement for persistent caches.
But why does <code>Ehcache</code> enforce this requirement and fail-fast if the requirement is violated?
What would have happened if we had proceeded with the sample code?
Would it have failed? If yes, then where?</p>
</div>
<div class="paragraph">
<p>The above piece of code would have failed in step 9 because the cache would not be able to retrieve the persisted data.
Because the serializer that you provided would fail in retrieving that data.
When the cache is reinitialized, the associated serializer instance is also initialized for the cache to work.
But the newly initialized serializer would have an empty state(empty <code>stringIdMap</code> and <code>idStringMap</code> maps and the <code>id</code>
initialized to 0).
So when the cache tries to read a value it gets an integer value from the persistent tier as that is what got persisted.
But using the empty state the serializer will not able to map that value to a fruit name, and so it would throw.
That leaves the persisted data unusable.
So what could you have done differently to make it work?</p>
</div>
<div class="paragraph">
<p>The answer is simple.
Persist the serializer’s state as well and restore it when the cache is re-initialized.
And that is exactly what persistent serializers would do.</p>
</div>
</div>
<div class="sect2">
<h3 id="persistent-serializers"><a class="anchor" href="#persistent-serializers"></a>Persistent serializers</h3>
<div class="ulist">
<ul>
<li>
<p>A persistent serializer persists its state and retrieves it when reinitialized.</p>
</li>
<li>
<p>A persistent serializer implementation can choose to persist the data wherever it wants.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>But a recommended way is to use the cache manager’s <code>LocalPersistenceService</code> so that the cache manager would take care
of the persistence.
Inorder to do that, the serializer implementation needs to have a constructor that takes in a
<code>FileBasedPersistenceContext</code> as an argument, in addition to the class loader argument.
The use of the <code>FileBasedPersistenceContext</code> argument is optional.
But the presence of this double-argument constructor is a strict requirement for persistent caches.
When the cache using this serializer is initialized, this 2-argument constructor is used to instantiate the serializer.</p>
</div>
<div class="paragraph">
<p>Have a look at this implementation of a persistent serializer.
It is just an extension of the same old transient serializer with the persistent stuff wired in.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">public class SimplePersistentStringSerializer extends SimpleTransientStringSerializer implements Closeable {
private final File stateFile;
public SimplePersistentStringSerializer(final ClassLoader loader, FileBasedPersistenceContext persistence) throws IOException, ClassNotFoundException {
super(loader);
stateFile = new File(persistence.getDirectory(), "serializer.data");
if(stateFile.exists()) {
restoreState();
}
}
@Override
public void close() throws IOException {
persistState();
}
private void restoreState() throws IOException, ClassNotFoundException {
FileInputStream fin = new FileInputStream(stateFile);
try {
ObjectInputStream oin = new ObjectInputStream(fin);
try {
idStringMap = (Map<Integer, String>) oin.readObject();
stringIdMap = (Map<String, Integer>) oin.readObject();
id = oin.readInt();
} finally {
oin.close();
}
} finally {
fin.close();
}
}
private void persistState() throws IOException {
OutputStream fout = new FileOutputStream(stateFile);
try {
ObjectOutputStream oout = new ObjectOutputStream(fout);
try {
oout.writeObject(idStringMap);
oout.writeObject(stringIdMap);
oout.writeInt(id);
} finally {
oout.close();
}
} finally {
fout.close();
}
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In the above persistent serializer, the state or the serialization/deserialization has not changed.
The only additional thing is the persistence logic. And that is fairly simple too.
The state is restored on initialization if one is found, and persisted on close.
And have a look at the sample from the previous section modified to use our persistent serializer.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">CacheConfiguration<Long, String> cacheConfig =
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES).disk(10, MemoryUnit.MB, true))
.withValueSerializer(SimplePersistentStringSerializer.class) <i class="conum" data-value="1"></i><b>(1)</b>
.build();
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(new CacheManagerPersistenceConfiguration(new File(PERSISTENCE_PATH)))
.withCache("fruitsCache", cacheConfig)
.build(true);
Cache<Long, String> fruitsCache = cacheManager.getCache("fruitsCache", Long.class, String.class);
fruitsCache.put(1L, "apple");
fruitsCache.put(2L, "mango");
fruitsCache.put(3L, "orange");
assertThat(fruitsCache.get(1L), is("apple"));
cacheManager.close();
cacheManager.init();
fruitsCache = cacheManager.getCache("fruitsCache", Long.class, String.class);
assertThat(fruitsCache.get(1L), is("apple"));</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The only change from the previous sample is the usage of <code>SimplePersistentStringSerializer</code> here.</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="third-party-serializers"><a class="anchor" href="#third-party-serializers"></a>Third-party serializers</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Ehcache by-default relies on a tweaked form of java standard serialization to perform serialization and deserialization.
But most of you already know that java built-in serialization is not the best performing serialization technique.
A lot of alternative serialization techniques are available in the market.
With the custom serializers support of ehcache you can take advantage of any one of those third-party serializers out
there and use those within ehcache.
All you have to do is write a custom serializer using the third-party serializer of your choice.</p>
</div>
<div class="paragraph">
<p>In-order to demonstrate that, I have written a custom serializer using the popular serialization framework <strong>Kryo</strong>.
Samples used in this section are not the same fruits cache based ones.
Here I’m using an employee cache of type <code>Cache<Long, Employee></code>.
I have kept the <code>Employee</code> object as simple as possible and yet represent a real-life object structure.
These are the structures that we have used:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">public class Description {
String alias;
int id;
public Description() {}
public Description(final String alias, final int id) {
this.alias = alias;
this.id = id;
}
@Override
public boolean equals(final Object obj) {
if(this == obj) return true;
if(obj == null || this.getClass() != obj.getClass()) return false;
Description other = (Description)obj;
if(id != other.id) return false;
if ((alias == null) ? (alias != null) : !alias.equals(other.alias)) return false;
return true;
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + id;
result = 31 * result + (alias == null ? 0 : alias.hashCode());
return result;
}
@Override
public String toString() {
return alias + ";" + id;
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">public class Person {
String name;
int age;
Description desc;
public Person() {}
public Person(String name, int age, Description desc) {
this.name = name;
this.age = age;
this.desc = desc;
}
@Override
public boolean equals(final Object other) {
if(this == other) return true;
if(other == null) return false;
if(!(other instanceof Person)) return false;
Person that = (Person)other;
if(age != that.age) return false;
if((name == null) ? (that.name != null) : !name.equals(that.name)) return false;
return true;
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + age;
result = 31 * result + (name == null ? 0 : name.hashCode());
return result;
}
@Override
public String toString() {
return name + ";" + age + "::" + desc;
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">public class Employee extends Person {
long employeeId;
public Employee() {}
public Employee(long employeeId, String name, int age, Description desc) {
super(name, age, desc);
this.employeeId = employeeId;
}
@Override
public boolean equals(final Object obj) {
if (!super.equals(obj)) return false;
if(!(obj instanceof Employee)) return false;
Employee other = (Employee)obj;
if(employeeId != other.employeeId) return false;
return true;
}
@Override
public int hashCode() {
return (31 * (int)employeeId) + super.hashCode();
}
@Override
public String toString() {
return employeeId + ";" + super.toString();
}
}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
None of the above classes are <code>Serializable</code>. Yet they can be serialized with Kryo. But for that every class needs
a no-arg constructor and these classes meet that requirement.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>So here is the kryo based custom serializer:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">public class KryoSerializer implements Serializer<Employee> {
private static final Kryo kryo = new Kryo();
public KryoSerializer(ClassLoader loader) {
//no-op
}
@Override
public ByteBuffer serialize(final Employee object) throws SerializerException {
Output output = new Output(4096);
kryo.writeObject(output, object);
return ByteBuffer.wrap(output.getBuffer());
}
@Override
public Employee read(final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
Input input = new Input(new ByteBufferInputStream(binary)) ;
return kryo.readObject(input, Employee.class);
}
@Override
public boolean equals(final Employee object, final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
return object.equals(read(binary));
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The above serializer is a state-less one that demonstrates the basic integration with kryo.
Here is the sample code that uses the same:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);
CacheConfiguration<Long, Employee> cacheConfig =
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, Employee.class, ResourcePoolsBuilder.heap(10))
.withValueSerializer(KryoSerializer.class) <i class="conum" data-value="1"></i><b>(1)</b>
.build();
Cache<Long, Employee> employeeCache = cacheManager.createCache("employeeCache", cacheConfig);
Employee emp = new Employee(1234, "foo", 23, new Description("bar", 879));
employeeCache.put(1L, emp);
assertThat(employeeCache.get(1L), is(emp));</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Here we configure the <code>KryoSerializer</code> for the <strong>VALUE</strong>.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Using some advanced features of kryo I managed to write the <em>transient</em> only and <em>persistent</em> only versions too.</p>
</div>
<div class="paragraph">
<p>Here is the transient one:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">public class TransientKryoSerializer implements Serializer<Employee>, Closeable{
protected static final Kryo kryo = new Kryo();
protected Map<Class, Integer> objectHeaderMap = new HashMap<Class, Integer>(); <i class="conum" data-value="1"></i><b>(1)</b>
public TransientKryoSerializer() {
}
public TransientKryoSerializer(ClassLoader loader) {
populateObjectHeadersMap(kryo.register(Employee.class)); <i class="conum" data-value="2"></i><b>(2)</b>
populateObjectHeadersMap(kryo.register(Person.class)); <i class="conum" data-value="3"></i><b>(3)</b>
populateObjectHeadersMap(kryo.register(Description.class)); <i class="conum" data-value="4"></i><b>(4)</b>
}
protected void populateObjectHeadersMap(Registration reg) {
objectHeaderMap.put(reg.getType(), reg.getId()); <i class="conum" data-value="5"></i><b>(5)</b>
}
@Override
public ByteBuffer serialize(Employee object) throws SerializerException {
Output output = new Output(new ByteArrayOutputStream());
kryo.writeObject(output, object);
output.close();
return ByteBuffer.wrap(output.getBuffer());
}
@Override
public Employee read(final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
Input input = new Input(new ByteBufferInputStream(binary)) ;
return kryo.readObject(input, Employee.class);
}
@Override
public boolean equals(final Employee object, final ByteBuffer binary) throws ClassNotFoundException, SerializerException {
return object.equals(read(binary));
}
@Override
public void close() throws IOException {
objectHeaderMap.clear();
}
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This <strong>objectHeaderMap</strong> is the state of the serializer. When an object is serialized the fully qualified name of the
class is written in the header. Since writing the entire name is costly I decided to map these names to some integer
values and then write out that integer instead of the name. So this map would contain the mapping between the
classes and the corresponding integer values.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Here we register a class with kryo and then kryo will assign an integer value to that class so that all instances of
class will be serialized with this assigned integer in-place of the fully-qualified class name. The <code>Employee</code> class
in this case. Refer <a href="https://github.com/EsotericSoftware/kryo#registration">Kryo#Registartion</a> for more information.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Since <code>Employee</code> extends <code>Person</code> we register that too.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Since the <code>Person</code> class contain a <code>Description</code> instance we register that too. So the idea is to register all
known custom class types associated with the object to be serialized(the employee object).</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>This is how we populate the <strong>objectHeaderMap</strong> every time we register a class.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The following sample is the same as the one in the previous section with just the serializer changed:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);
CacheConfiguration<Long, Employee> cacheConfig =
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, Employee.class, ResourcePoolsBuilder.heap(10))
.withValueSerializer(TransientKryoSerializer.class)
.build();
Cache<Long, Employee> employeeCache = cacheManager.createCache("employeeCache", cacheConfig);
Employee emp = new Employee(1234, "foo", 23, new Description("bar", 879));
employeeCache.put(1L, emp);
assertThat(employeeCache.get(1L), is(emp));</code></pre>
</div>
</div>
<div class="paragraph">
<p>The above sample must be self explanatory as we have already seen this sample so many times.</p>
</div>
<div class="paragraph">
<p>And now the persistent adaptation of the transient serializer is here:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">public class PersistentKryoSerializer extends TransientKryoSerializer {
private final File stateFile;
public PersistentKryoSerializer(ClassLoader loader, FileBasedPersistenceContext persistence) throws IOException, ClassNotFoundException {
stateFile = new File(persistence.getDirectory(), "PersistentKryoSerializerState.ser");
if(stateFile.exists()) { <i class="conum" data-value="1"></i><b>(1)</b>
restoreState(); <i class="conum" data-value="2"></i><b>(2)</b>
for(Map.Entry<Class, Integer> entry: objectHeaderMap.entrySet()) { <i class="conum" data-value="3"></i><b>(3)</b>
kryo.register(entry.getKey(), entry.getValue()); <i class="conum" data-value="4"></i><b>(4)</b>
}
}
}
@Override
public void close() throws IOException {
persistState(); <i class="conum" data-value="5"></i><b>(5)</b>
}
private void persistState() throws FileNotFoundException {
Output output = new Output(new FileOutputStream(stateFile));
try {
kryo.writeObject(output, objectHeaderMap);
} finally {
output.close();
}
}
private void restoreState() throws FileNotFoundException {
Input input = new Input(new FileInputStream(stateFile));
try {
objectHeaderMap = kryo.readObject(input, HashMap.class);
} finally {
input.close();
}
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>You must be familiar with this routine already:</p>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>On initialization, if a persistent file is found…​</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Restore the contents of the file which essentially restores the <strong>objectHeaderMap</strong></td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Then iterate through the contents of the map and…​</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Register the types again with <strong>kryo</strong> using the same integer mapped values. Then only the persisted data can be
deserialized as they are persisted with these integer values in their headers.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>On <em>close</em>, the map is serialized and persisted to a file.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>And the familiar test sample again testing this persistent serializer implementation:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="java">CacheConfiguration<Long, Employee> cacheConfig =
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, Employee.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES).offheap(5, MemoryUnit.MB).disk(10, MemoryUnit.MB, true))
.withValueSerializer(PersistentKryoSerializer.class)
.build();
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(new CacheManagerPersistenceConfiguration(new File(PERSISTENCE_PATH)))
.withCache("employeeCache", cacheConfig)
.build(true);
Cache<Long, Employee> employeeCache = cacheManager.getCache("employeeCache", Long.class, Employee.class);
Employee emp = new Employee(1234, "foo", 23, new Description("bar", 879));
employeeCache.put(1L, emp);
assertThat(employeeCache.get(1L), is(emp));
cacheManager.close();
cacheManager.init();
employeeCache = cacheManager.getCache("employeeCache", Long.class, Employee.class);
assertThat(employeeCache.get(1L), is(emp));</code></pre>
</div>
</div>
</div>
</div>
<p><a href="https://www.ehcache.org/blog/2016/05/12/ehcache3-serializers.html">Custom Serializers</a> was originally published by Albin Suresh at <a href="https://www.ehcache.org">Ehcache</a> on May 12, 2016.</p>https://www.ehcache.org/resources/2016/05/02/ehcache-released2016-05-02T00:00:00+00:002016-05-02T00:00:00+00:00https://www.ehcache.org<div class="paragraph">
<p>An <a href="https://www.infoq.com/news/2016/05/ehcache-3.0">article about the Ehcache 3 release</a> on InfoQ by <a href="https://twitter.com/mraible">Matt Raible</a></p>
</div>
<p><a href="https://www.ehcache.org/resources/2016/05/02/ehcache-released.html">Ehcache 3.0 Released with Revamped API and Off-Heap Storage</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on May 02, 2016.</p>https://www.ehcache.org/resources/2016/04/22/terracotta-offheap2016-04-22T00:00:00+00:002016-04-22T00:00:00+00:00https://www.ehcache.org<div class="paragraph">
<p>This content is in French.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Conference page: <a href="https://cfp.devoxx.fr/2016/talk/LBA-4042/Terracotta_Off-Heap_pour_les_nuls" class="bare">https://cfp.devoxx.fr/2016/talk/LBA-4042/Terracotta_Off-Heap_pour_les_nuls</a></p>
</li>
<li>
<p>Video: <a href="https://www.youtube.com/watch?v=ezTrDlrIz5o" class="bare">https://www.youtube.com/watch?v=ezTrDlrIz5o</a></p>
</li>
</ul>
</div>
<p><a href="https://www.ehcache.org/resources/2016/04/22/terracotta-offheap.html">Terracotta Off-Heap pour les nuls</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on April 22, 2016.</p>https://www.ehcache.org/resources/2016/04/21/ehcache-element2016-04-21T00:00:00+00:002016-04-21T00:00:00+00:00https://www.ehcache.org<div class="paragraph">
<p>This content is in French.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Conference page: <a href="https://cfp.devoxx.fr/2016/talk/FFI-0883/Ehcache_hors_de_son_Element" class="bare">https://cfp.devoxx.fr/2016/talk/FFI-0883/Ehcache_hors_de_son_Element</a></p>
</li>
<li>
<p>Video: <a href="https://www.youtube.com/watch?v=7Ytt93mGrMY" class="bare">https://www.youtube.com/watch?v=7Ytt93mGrMY</a></p>
</li>
</ul>
</div>
<p><a href="https://www.ehcache.org/resources/2016/04/21/ehcache-element.html">Ehcache hors de son Element</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on April 21, 2016.</p>https://www.ehcache.org/resources/2015/11/18/ehcache3-jsr1072015-11-18T00:00:00+00:002015-11-18T00:00:00+00:00https://www.ehcache.org<div class="ulist">
<ul>
<li>
<p>Conference page: <a href="http://cfp2015.devoxx.ma/2015/talk/FDX-7262/Ehcache3:_JSR107_on_steroids" class="bare">http://cfp2015.devoxx.ma/2015/talk/FDX-7262/Ehcache3:_JSR107_on_steroids</a></p>
</li>
<li>
<p>Slides: <a href="http://www.slideshare.net/LouisJacomet/ehcache-3-jsr107-on-steroids-at-devoxx-morocco" class="bare">http://www.slideshare.net/LouisJacomet/ehcache-3-jsr107-on-steroids-at-devoxx-morocco</a></p>
</li>
</ul>
</div>
<p><a href="https://www.ehcache.org/resources/2015/11/18/ehcache3-jsr107.html">Ehcache 3: JSR-107 on steroids</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on November 18, 2015.</p>https://www.ehcache.org/resources/2015/11/13/terracotta-offHeap2015-11-13T00:00:00+00:002015-11-13T00:00:00+00:00https://www.ehcache.org<div class="ulist">
<ul>
<li>
<p>Conference page: <a href="https://cfp.devoxx.be/2015/talk/AYH-2795/Terracotta’s_OffHeap_Explained" class="bare">https://cfp.devoxx.be/2015/talk/AYH-2795/Terracotta’s_OffHeap_Explained</a></p>
</li>
<li>
<p>Video: <a href="https://www.youtube.com/watch?v=xCojSZMp_Zw" class="bare">https://www.youtube.com/watch?v=xCojSZMp_Zw</a></p>
</li>
<li>
<p>Slides: <a href="http://www.slideshare.net/ChrisDennis7/terracottas-offheap-explained-55214150" class="bare">http://www.slideshare.net/ChrisDennis7/terracottas-offheap-explained-55214150</a></p>
</li>
</ul>
</div>
<p><a href="https://www.ehcache.org/resources/2015/11/13/terracotta-offHeap.html">Terracotta’s OffHeap Explained</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on November 13, 2015.</p>https://www.ehcache.org/resources/2015/11/09/caching-1012015-11-09T00:00:00+00:002015-11-09T00:00:00+00:00https://www.ehcache.org<div class="ulist">
<ul>
<li>
<p>Conference page: <a href="https://cfp.devoxx.be/2015/talk/KVW-1306/Caching_101:_Caching_on_the_JVM_(and_beyond).html" class="bare">https://cfp.devoxx.be/2015/talk/KVW-1306/Caching_101:_Caching_on_the_JVM_(and_beyond).html</a></p>
</li>
<li>
<p>Video: <a href="https://www.youtube.com/watch?v=HtrU07c17JQ" class="bare">https://www.youtube.com/watch?v=HtrU07c17JQ</a></p>
</li>
</ul>
</div>
<p><a href="https://www.ehcache.org/resources/2015/11/09/caching-101.html">Caching 101: Caching on the JVM (and beyond)</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on November 09, 2015.</p>https://www.ehcache.org/resources/2015/10/19/javax-cache-to-your-app2015-10-19T00:00:00+00:002015-10-19T00:00:00+00:00https://www.ehcache.org<div class="ulist">
<ul>
<li>
<p>Conference page: <a href="https://spring.io/blog/2015/10/19/springone2gx-2015-replay-bringing-javax-cache-ing-to-your-application" class="bare">https://spring.io/blog/2015/10/19/springone2gx-2015-replay-bringing-javax-cache-ing-to-your-application</a></p>
</li>
<li>
<p>Video: <a href="https://spring.io/blog/2015/10/19/springone2gx-2015-replay-bringing-javax-cache-ing-to-your-application" class="bare">https://spring.io/blog/2015/10/19/springone2gx-2015-replay-bringing-javax-cache-ing-to-your-application</a></p>
</li>
<li>
<p>Slides: <a href="http://www.slideshare.net/SpringCentral/bringing-javaxcacheing-to-your-app" class="bare">http://www.slideshare.net/SpringCentral/bringing-javaxcacheing-to-your-app</a></p>
</li>
</ul>
</div>
<p><a href="https://www.ehcache.org/resources/2015/10/19/javax-cache-to-your-app.html">Bringing javax.cache'ing to your application</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on October 19, 2015.</p>https://www.ehcache.org/resources/2015/06/22/caching-reboot2015-06-22T00:00:00+00:002015-06-22T00:00:00+00:00https://www.ehcache.org<div class="ulist">
<ul>
<li>
<p>Conference page: <a href="http://cfp.2015.devoxx.pl/2015/talk/QYO-0948/Caching_reboot:_javax.cache_&_Ehcache_3.html" class="bare">http://cfp.2015.devoxx.pl/2015/talk/QYO-0948/Caching_reboot:_javax.cache_&_Ehcache_3.html</a></p>
</li>
<li>
<p>Video: <a href="https://www.youtube.com/watch?v=6kW7tcqvsVs" class="bare">https://www.youtube.com/watch?v=6kW7tcqvsVs</a></p>
</li>
<li>
<p>Slides: <a href="http://www.slideshare.net/LouisJacomet/caching-reboot-javaxcache-ehcache-3-61757652" class="bare">http://www.slideshare.net/LouisJacomet/caching-reboot-javaxcache-ehcache-3-61757652</a></p>
</li>
</ul>
</div>
<p><a href="https://www.ehcache.org/resources/2015/06/22/caching-reboot.html">Caching reboot: javax.cache & Ehcache 3</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on June 22, 2015.</p>https://www.ehcache.org/resources/2015/04/09/caching-reboot2015-04-09T00:00:00+00:002015-04-09T00:00:00+00:00https://www.ehcache.org<div class="paragraph">
<p>This content is in French.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Conference page: <a href="https://www.devoxx.fr/2015/talk/AIM-2044/Caching_reboot:_javax.cache_&_Ehcache_3.html" class="bare">https://www.devoxx.fr/2015/talk/AIM-2044/Caching_reboot:_javax.cache_&_Ehcache_3.html</a></p>
</li>
<li>
<p>Video: <a href="https://www.youtube.com/watch?v=cnj7b98KeRU" class="bare">https://www.youtube.com/watch?v=cnj7b98KeRU</a></p>
</li>
<li>
<p>Slides: <a href="http://www.slideshare.net/LouisJacomet/caching-reboot-javaxcache-ehcache-3" class="bare">http://www.slideshare.net/LouisJacomet/caching-reboot-javaxcache-ehcache-3</a></p>
</li>
</ul>
</div>
<p><a href="https://www.ehcache.org/resources/2015/04/09/caching-reboot.html">Caching reboot: javax.cache & Ehcache 3</a> was originally published by at <a href="https://www.ehcache.org">Ehcache</a> on April 09, 2015.</p>