How to enable ehCache (3.x) in SpringMVC (5.x) project - java

In our project we use the ehcache 2.x and now we want to migrate to 3.x but somehow I fail tremendously. In 2.x we have the configuration in ehcache.xml. To what I read is that I need to change the content as well when using 3.x.
But first of all I have no clue how to wire the Cache by itself. I have
#EnableCaching
#Configuration
public class CacheConf {
#Bean
public CacheManager cacheManager() {
final CacheManagerBuilder<org.ehcache.CacheManager> ret = CacheManagerBuilder.newCacheManagerBuilder();
ret.withCache("properties", getPropCache());
ret.withCache("propertyTypes", getPropCache());
return (CacheManager) ret.build(true);
}
....
But this configuration I get a java.lang.ClassCastException: org.ehcache.core.EhcacheManager cannot be cast to org.springframework.cache.CacheManager which means that I cannot set the ehcache properly.
Note: In 2.x we configured different types of caches, more or less due to the expiration period - and since other object types are stored therein.
Hints to get it running, cause I didn't find a working sample with Spring 5 and ehcache 3.x? Best would be a config without xml!

The Ehcache CacheManager isn't a Spring CacheManager. This is why you get the ClassCastException.
Ehcache 3 is JSR107 (JCache) compliant. So Spring will connect to it using that.
You will find an example here and a simpler one in Spring Boot petclinic.
If I take your example, you will do something like
#EnableCaching
#Configuration
public class CacheConf {
#Bean
public CacheManager cacheManager() {
EhcacheCachingProvider provider = (EhcacheCachingProvider) Caching.getCachingProvider();
Map<String, CacheConfiguration<?, ?>> caches = new HashMap<>();
caches.put("properties", getPropCache());
caches.put("propertyTypes", getPropCache());
DefaultConfiguration configuration = new DefaultConfiguration(caches, provider.getDefaultClassLoader());
return new JCacheCacheManager(provider.getCacheManager(provider.getDefaultURI(), configuration));
}
private CacheConfiguration<?, ?> getPropCache() {
// access to the heap() could be done directly cause this returns what is required!
final ResourcePoolsBuilder res = ResourcePoolsBuilder.heap(1000);
// Spring does not allow anything else than Objects...
final CacheConfigurationBuilder<Object, Object> newCacheConfigurationBuilder = CacheConfigurationBuilder
.newCacheConfigurationBuilder(Object.class, Object.class, res);
return newCacheConfigurationBuilder.build();
}
}

Related

Spring Caching Implementation in a Java app?

In my Java app that based on Spring Boot, I am trying to implement a caching mechanism for the following service method:
#Override
public List<EmployeeDTO> findAllByCountry(Country country) {
final Map<Pair<UUID, String>, List<CountryTranslatable>> valueList
= countryRepository...
// code omitted for brevity
}
After several examples regarding to this issue, I decided on the approach mentioned on A Guide To Caching in Spring.
However, I am a little bit confused as it contains Spring and Spring Boot implementations and uses different annotation examples. I think I should start from 3.1. Using Spring Boot section as I use Spring Boot, but I am not sure about which Caching Annotation I should use (4.1. #Cacheable seems to be ok but I am not sure).
So, where should I put SimpleCacheCustomizer and how can I apply that approach for my service method above (findAllByCountry)? Any simple example would really be appreciated as I am new in Spring.
You don't need any customizations if you are a starter, and you want only the basics then do the following
#Configuration
#EnableCaching
public class CachingConfig {
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}
The provided article states, return new ConcurrentMapCacheManager("addresses"); but you can use the default constructor and the relevant cache for adresses will be created later with #Cacheable("addresses"). So no need for this to be in configuration.
You also need
#Cacheable("employeesList")
#Override
public List<EmployeeDTO> findAllByCountry(Country country) {
final Map<Pair<UUID, String>, List<CountryTranslatable>> valueList
= countryRepository...
// code omitted for brevity
}
ready to go, that is the basic setup
If you want to customize the autoconfigured cachemanager then only you should implement CacheManagerCustomizer interface.
In usual cases you don't need to customize the autoconfigured cachemanager. The example has been given in the link you attached.
Your understanding on the cacheable annotation is also correct and it should work fine for you.
You can put that component class with other classes in the component scan range.
You should put your SimpleCacheCustomizer along with your others Spring configuration class. That way, your component will be scanned and loaded by Spring.
#Component
public class SimpleCacheCustomizer
implements CacheManagerCustomizer<ConcurrentMapCacheManager> {
#Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setCacheNames(asList("employeesList", "otherCacheName"));
}
}
To use the cache with your service, add the annotation #Cacheable("employeesList")
#Cacheable("employeesList")
#Override
public List<EmployeeDTO> findAllByCountry(Country country) {
final Map<Pair<UUID, String>, List<CountryTranslatable>> valueList
= countryRepository...
// code omitted for brevity
}
If you want to verify the cache is working, just enable sql_query in your Spring configuration and check that findAllByCountry is no longer making any request to the DB.

Why Redis Cache is not getting empty in my Spring Boot application?

I am using Redis Cache in my Spring Boot application to store data of multiple rest API's.
I am clearing Redis cache on a regular interval using Spring Cron Jobs. The method is getting called at required time-slots.
I have verified the logs but the cache is not getting clear and hence it is showing the stale data.
The code where I'm trying to clear the cache.
public class CustomerDerivation {
#Autowired
#Qualifier("redisCacheMngr")
CacheManager redisCacheMngr;
#Scheduled(cron = "${redis.api.update.interval}")
#CacheEvict(value = "redis-cache", allEntries = true, cacheNames = {"redis-cache"})
protected void cacheEvict() {
redisCacheMngr.getCache("redis-cache").clear();
logger.info("Evicting ModelCache");
}
}
Custom cache configuration code.
#Configuration
#Profile("cloud")
public class CacheConfig extends AbstractCloudConfig {
#Autowired
Environment env;
#Bean
public RedisConnectionFactory brRedisFactory() {
return connectionFactory().redisConnectionFactory(env.getProperty("model_cache_name"));
}
#Bean
public RedisTemplate<String, Object> brRedisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(brRedisFactory());
return redisTemplate;
}
#Bean(name = "redisCacheMngr")
public CacheManager cacheManager() {
RedisCacheManager cacheManager = new RedisCacheManager(brRedisTemplate());
cacheManager.setUsePrefix(true);
cacheManager.setTransactionAware(true);
return cacheManager;
}
}
How to fix the code to clear the redis cache ?
1) Have you enabled the cache using #EnableCaching annotation?
2) why are you using #CacheEvict and clear() in the same method? Both serves the same purpose. Use either one of them.
Check the following:
App is started with the according profile (ie. "cloud")
#EnabledCaching on your configuration
Avoid to mix #CacheEvict and the CacheManager bean, you have to choose one way for eviction
Extract the #Scheduled method in another "CronJobs" bean to avoid multiple annotations and AOP inside class issues.
Regards.
Spring use Spring AOP (Aspect Oriented Programming) to implement caching which means you must use public access level on your cacheEvict() method so it can be intercepted by AOP proxy. Otherwise it is like you never annotate your method with #CacheEvict

Unable to look at cache metrics(hit/miss/size) in spring boot?

I have implemented two cacheManagers.
One using caffiene and one using redis.
I have exposed them as beans and they are working as expected of them.
There isn't any cache endpoint in the list available at /actuator/metrics path either. I was able to only load /actuator/caches and /actuator/caches/{cacheName} endpoint. These endpoint only show the name and class of the cache being used. I am unable to see any metrics related to them.
I am using springboot 2.1.3 and spring-boot-actuator.
#Bean
public CacheManager caffeine() {
CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
caffeineCacheManager.setCaffeine(caffeineCacheBuilder());
return caffeineCacheManager;
}
private Caffeine<Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder().
initialCapacity(initialCapacity).
maximumSize(maxCapacity).
expireAfterAccess(Duration.parse(ttl)).
recordStats();
}
Turned out #Cacheable makes the cache dynamically at runtime, thus if we want the annotated caches to have metrics, we have to set the cache names explicitly in the cacheManagerBean which inturn makes the cacheManager static.
Or create a custom registryBinder as stated in after upgrade to Spring Boot 2, how to expose cache metrics to prometheus?
and call the register method only after the cache is created.

EhCache 3 and Spring Boot

Is there a way to abstract the EhCache 3 CacheManager (org.ehcache.CacheManager) to Spring's CacheManager (org.springframework.cache.CacheManager)?
With EhCache2, it is possible, by the following:
#Bean
public org.springframework.cache.CacheManager cacheManager(net.sf.ehcache.CacheManager ehcache) {
EhCacheCacheManager cacheManager = new EhCacheCacheManager(ehcache);
return cacheManager;
}
HINT: I've found a way to abstract the javax.cache.CacheManager cacheManager to the Spring's CacheManager (org.springframework.cache.CacheManager), by:
#Bean
public org.springframework.cache.CacheManager cacheManager(javax.cache.CacheManager cacheManager) {
return new JCacheCacheManager(cacheManager);
}
It will be also helpful if there is a way to cast org.ehcache.CacheManager to javax.cache.CacheManager .
Thanks.
Yes, you should rely on the standard Java caching specification, JSR-107, aka JCache (javax.cache.CacheManager)
Spring has a nice integration of it, and Ehcache2 and Ehcache3 also are compatible with it
Please have a look at this answer, it also comes with some simple examples : https://stackoverflow.com/a/39340151/24069

Using a Spring CacheManager from dependent jar

I have several WAR projects that include a dependency to a certain utility JAR project. I would like to be able to cache certain methods from the utility project by using spring's #Cacheable annotation, so I tried creating a configuration file on the utility project where I could define a CacheManager bean that could handle the caching of the methods. The configuration file looks like this:
(Note that I'm using a Redis caching implementation, but the spring config should be very similar for other caching providers)
#Configuration
#EnableCaching
public class RedisConfig {
#Value("${redis.hostUrl}")
private String hostUrl = null;
#Value("${redis.port}")
private Integer port = null;
#Value("${redis.defaultExpiration}")
private Integer defaultExpiration = null;
#Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory = new JedisConnectionFactory();
// Defaults
redisConnectionFactory.setHostName(hostUrl);
redisConnectionFactory.setPort(port);
return redisConnectionFactory;
}
#Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(cf);
return redisTemplate;
}
#Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
// Number of seconds before expiration. Defaults to unlimited (0)
cacheManager.setDefaultExpiration(defaultExpiration);
cacheManager.setUsePrefix(true);
List<String> cacheNames = new ArrayList<String>();
cacheNames.add("testing");
cacheManager.setCacheNames(cacheNames);
return cacheManager;
}
}
I'm pretty sure the configuration itself is not a problem because I have similar configuration files in other WAR modules and caching works fine on those. However, since this is a JAR that gets loaded by other modules, my guess is that the CacheManager is not being picked up by Spring.
In project A, which has a dependency to the utils project, I have a method like the following (edited for simplicity; ignore the invalid syntax):
#RequestMapping(...)
#ResponseBody
public Dto methodA(...) {
//The relevant part
testCachedMethod(value);
cachedMethodFromProjectB(value);
}
#Cacheable(value="testing")
public String testCachedMethod(String value) {
return value;
}
Project B is another WAR that has its own CacheManager (not tied to utils), and has similar methods cached using #Cacheable. If I hit Project A's methodA, the caches from Project B get stored properly, but the cachedMethod from Project A does not store anything in the cache (and neither do the methods from the utils project annotated with #Cacheable). I also tried the other way around, creating the CacheManager directly on Project A, but it also fails to cache the annotated methods inside the utils project (and I'd prefer declaring the manager on the utils project so it can be reused by other modules).
I know Spring is properly initializing the beans in the utils project because project A fails to run if its context's property placeholder does not find the property files for the #Value annotations from the cache Config file. So I'd suppose the CacheManager bean is there, but somehow it doesn't work. Any ideas on what I'm doing wrong or if it's even possible to use a CacheManager from a dependent JAR (if possible, using Java configuration)? I'm using Spring 3.2.0.RELEASE.
Been trying to figure this out for a couple days now, so any help is greatly appreciated.
Turned out to be an issue with the spring cache abstraction's default proxy mode. All the methods whose caching annotations were not working were being called by other methods within the same object. I can properly manipulate the caches from the dependent jar project using #Autowire on the CacheManager bean and manually performing the caching operations. While it should be possible to use the annotations by enabling the AspectJ weaving mode, I don't want to add more complexity by having to deal with the weaving in the dependent modules.

Categories