Caching using Ehcache Java Library

Ehcache from Terracotta is one of Java's most widely used Cache. It is concurrent and highly scalable. It has small footprint with SL4J as the only dependencies. It supports multiple strategies like Expiration policies, Eviction policies. It supports three storage tiers, heap, off-heap, disk storage. There are very few caching products supports multiple tier storage. If you want to scale, you cannot store all items in heap there should be support for off-heap and disk storage. Ehcache is licensed under Apache 2.0. In this article, we can see about basic usage of Ehcache.
Maven dependency
<dependency>
<groupId>org.Ehcache</groupId>
<artifactId>Ehcache</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.1.0</version>
</dependency>
Let's create and initialize two cache instance. Before creating cache, we need to create Cache Manager instance which in-turn manages the cache.
Create Cache Manager instance and initialize it.
CacheManager cacheManager = CacheManagerBuilder
.newCacheManagerBuilder()
.build();
cacheManager.init();
Using Cache Manager, create a cache instance which takes String as Key and User object as value. Using this cache we can store user objects for token or session. If you are building Rest application and authenticating using JWT token then you may need a cache which stores some information with respect to token. Below statement will create a cache with 100 entries in heap and the entry will stay for an hour beyond that it will be evicted. If user defined objects has to be cached then object should be serializable.
Cache<String, User>sessionCache = cacheManager.createCache(SESSION_CACHE,
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class,
User.class, ResourcePoolsBuilder.heap(100))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofHours(1))));
Below statement creates a cache with String type as key and value. The cache can hold 10 MB of data in the heap and beyond that it will maintain 50 MB in off-heap. So total 60 MB of memory will be available.
Cache<String, String>dbCache = cacheManager.createCache(DB_CACHE,
CacheConfigurationBuilder
.newCacheConfigurationBuilder(String.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, MemoryUnit.MB)
.offheap(50, MemoryUnit.MB)
.build()));
Resource Pool builder decides how much resources to be used. First statement allocates 100 entries in heap, second one allocates 10 MB in heap, third statement allocates 10 MB in off-heap and fourth one allocates 10 MB in disk. If the cache entries are added beyond its total allocated resource pool then the older entry will be evicted.
1. ResourcePoolsBuilder.heap(100)
2. ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB)
3. ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB)
4. ResourcePoolsBuilder.newResourcePoolsBuilder().disk(10, MemoryUnit.MB)
Off Heap and Disk store
There are very few cache libraries provide off-heap and disk store. The off-heap is nothing but direct memory or memory mapped files. An application does various tasks, caching is required to speed up the process but it should not take whole a lot memory. It is good to use few resource in heap and off load remaining. Storing it in disk provides persistence. Caching is always temporary but if you want to persist after JVM restart then use disk store.
If you want to use disk then while creating Cache Manager, provide path to persist data.
cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence("<File-System-Path>"))
.build();
Now we created cache, let's use it to store values.
Cache<String, User>sessionCache = cacheManager.getCache(SESSION_CACHE, String.class, User.class);
sessionCache.put(sessionId, userObj);
If you want to retrieve,
Cache<String, User>sessionCache = cacheManager.getCache(SESSION_CACHE, String.class, User.class);
User userObj = sessionCache.get(sessionId);
Now let's take a look in to the complete source
public class User implements Serializable {
private String name;
private String role;
private int userId;
public User(int userId, String name, String role) {
this.userId = userId;
this.name = name;
this.role = role;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
import java.time.Duration;
import org.Ehcache.Cache;
import org.Ehcache.CacheManager;
import org.Ehcache.config.builders.CacheConfigurationBuilder;
import org.Ehcache.config.builders.CacheManagerBuilder;
import org.Ehcache.config.builders.ExpiryPolicyBuilder;
import org.Ehcache.config.builders.ResourcePoolsBuilder;
import org.Ehcache.config.units.MemoryUnit;
public class CacheService {
static CacheManager cacheManager;
static final String SESSION_CACHE = "session-cache";
static final String DB_CACHE = "db-cache";
public static void initialize() {
cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.build();
cacheManager.init();
Cache<String, User>sessionCache = cacheManager.createCache(SESSION_CACHE,
CacheConfigurationBuilder.newCacheConfigurationBuilder(
String.class,
User.class,
ResourcePoolsBuilder.heap(100))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofHours(1))));
Cache<String, String>dbCache = cacheManager.createCache(DB_CACHE,
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, MemoryUnit.MB)
.offheap(10, MemoryUnit.MB)
.build()));
}
public static void initializeWithPersistance() {
cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence("<File-System-Path>"))
.build();
cacheManager.init();
Cache<String, User>sessionCache = cacheManager.createCache(SESSION_CACHE,
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, User.class,
ResourcePoolsBuilder.heap(100).offheap(2, MemoryUnit.MB).disk(10, MemoryUnit.MB)));
Cache<String, String>dbCache = cacheManager.createCache(DB_CACHE,
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class,
ResourcePoolsBuilder.heap(10).offheap(2, MemoryUnit.MB)));
}
public static void close() {
cacheManager.close();
}
public static void saveUserSession(String sessionId, User userObj) {
Cache<String, User>sessionCache = cacheManager.getCache(SESSION_CACHE, String.class, User.class);
sessionCache.put(sessionId, userObj);
}
public static User getUserSession(String sessionId) {
Cache<String, User>sessionCache = cacheManager.getCache(SESSION_CACHE, String.class, User.class);
return sessionCache.get(sessionId);
}
public static void saveDbResults(String query, String resultsJson) {
Cache<String, String>dbCache = cacheManager.getCache(DB_CACHE, String.class, String.class);
dbCache.put(query, resultsJson);
}
public static String getDbResults(String query) {
Cache<String, String>dbCache = cacheManager.getCache(DB_CACHE, String.class, String.class);
return dbCache.get(query);
}
}
Reference:
http://www.Ehcache.org/apidocs/3.7.0/index.html