Difference between net.sf.ehcache and org.ehcache? - spring

What is the difference between net.sf.ehcache and org.ehcache?
The current version of net.sf.ehcache is 2.10.5 whereas same for org.ehcache is 3.5.2.
Spring uses net.sf.ehcache's CacheManager, and org.ehcache's CacheManager isn't compatible for same.
Is there any specific reason for this? Please explain.

As you can verify on the page http://www.ehcache.org/downloads/, Ehcache 3 is using the package prefix org.ehcache and Ehcache 2 is using the package prefix net.sf.ehcache. That's it.

There are different in many levels. With ehcache 3.x, Element is not there anymore. One should directly put the key and value in the Cache therefore you can provide types when you create cache:
Cache<Long, String> myCache = cacheManager.getCache("myCache", Long.class, String.class);
And consequently when retrieving the value, you avoid the hassle of getObjectValue instead you just treat Cache like a ConcurrentMap. So you won't get NullPointerException if the key doesn't exist, so you won't need check for cache.get(cacheKey) != null
cache.get(cacheKey);
The way to instantiate CacheManager has also changed. You won't getInstance so it is not singleton anymore. Instead you get a builder, which is way nicer, especially that you can provide it with configuration parameters inline:
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("preConfigured",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.heap(100))
.build())
.build(true);

Related

More than 1 caching storage in spring boot app

I am facing a strange issue - I have hazelcast and redis in my project. Suddenly all #Cacheable annotations are putting entries only to hazelcast cache, even if the particular cache name is configured via redis cache builder:
#Bean
fun redisCacheManagerBuilderCustomizer(): RedisCacheManagerBuilderCustomizer? {
return RedisCacheManagerBuilderCustomizer { builder: RedisCacheManagerBuilder ->
builder
.withCacheConfiguration(
MY_CACHE,
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(3))
)
}
}
Using cache:
#Cacheable(cacheNames = [CacheConfig.MY_CACHE])
#Cacheable(value= [CacheConfig.MY_CACHE])
Both does not work and forwards requests to hazelcast only. How to solve this? Using different cacheManager?
Typically, only 1 caching provider is in use to cache data, such as in the service or data access tier of your Spring [Boot] application using Spring's Cache Abstraction and infrastructure components, such as the CacheManager and caching annotations.
When multiple caching providers (e.g. Hazelcast and Redis) are on the classpath of your Spring Boot application, then it might be necessary to declare which caching provider (e.g. Redis) you want to [solely] use for caching purposes. With this arrangement, Spring Boot allows you to declare your intentions using the spring.cache.type property as explained in the ref doc, here (see first Tip). Valid values of this property are defined by the enumerated values in the CacheType enum.
However, if you want to cache data using multiple caching providers at once, then you need to explicitly declare your intentions using this approach as well.
DISCLAIMER: It has been awhile since I have traced through Spring Boot auto-configuration where caching is concerned, and how it specifically handles multiple caching providers on the application classpath, especially when a specific caching provider has not been declared, such as by explicitly declaring the spring.cache-type property. However, and again, this may actually be your intention, to use multiple caching providers in a single #Cacheable (or #CachePut) service or data access operation. If so, continue reading...
To do so, you typically use 1 of 2 approaches. These approaches are loosely described in the core Spring Framework's ref doc, here.
1 approach is to declare the cacheNames of the caches from each caching provider along with the CacheManager, like so:
#Service
class CustomerService {
#Cacheable(cacheNames = { "cacheOne", "cacheTwo" }, cacheManager="compositeCacheManager")
public Customer findBy(String name) {
// ...
}
}
In this case, "cacheOne" would be the name of the Cache managed by caching provider one (e.g. Redis), and "cacheTwo" would be the name of the Cache managed by caching provider two (i.e. "Hazelcast").
DISCLAIMER: You'd have to play around, but it might be possible to simply declare a single Cache name here (e.g. "Customers"), where the caches (or cache data structures in each caching provider) are named the same, and it would still work. I am not certain, but it seems logical this would work as well.
The key (no pun intended) to this example, however, is the declaration of the CacheManager using the cacheManager attribute of the #Cacheable annotation. As you know, the CacheManager is the Spring SPI infrastructure component used to find and manage Cache objects (caches from the caching providers) used for caching purposes in your Spring managed beans (such as CustomerService).
I named this CacheManager deliberately, "compositeCacheManager". Spring's Cache Abstraction provides the CompositeCacheManager implementation, which as the name suggests, composes multiple CacheManagers for use in single cache operation.
Therefore, you could do the following in you Spring [Boot] application configuration:
#Configuration
class MyCachingConfiguration {
#Bean
RedisCacheManager cacheManager() {
// ...
}
#Bean
HazelcastCacheManager hazelcastCacheManager() {
// ...
}
#Bean
CompositeCacheManager compositeCacheManager(RedisCacheManager redis, HazelcastCacheManager hazelcast) {
return new CompositeCacheManager(redis, hazelcast);
}
}
NOTE: Notice the RedisCacheManager is the "default" CacheManager declaration and cache provider (implementation) used when no cache provider is explicitly declared in a caching operation, since the bean name is "cacheManager".
Alternatively, and perhaps more easily, you can choose to implement the CacheResolver interface instead. The Javadoc is rather self-explanatory. Be aware of the Thread-safety concerns.
In this case, you would simply declare a CacheResolver implementation in your configuration, like so:
#Configuration
class MyCachingConfiguration {
#Bean
CacheResolver customCacheResolver() {
// return your custom CacheResolver implementation
}
}
Then in your application service components (beans), you would do:
#Service
class CustomerService {
#Cacheable(cacheNames = "Customers", cacheResolver="customCacheResolver")
public Customer findBy(String name) {
// ...
}
}
DISCLAIMER: I have not tested either approach I presented above here, but I feel reasonably confident this should work as expected. It may need some slight modifications, but should generally be the approach(es) you should follow.
If you have any troubles, please post back in the comments and I will try to follow up.

Want to refresh cache in Redis using Spring boot Considering multiple pods

Currently I am using annotation #CacheEvict using cron job, after that reloading again using new calls, but not happy with this approach as I have multiple nods.
My requirement is
Reload cache with new data after a certain time limit.
Should be working in multiple nodes.
Looking for a elegant design.
Did you try using cache manager?
https://www.baeldung.com/spring-multiple-cache-managers
Spring #CacheEvict annotation is used to evict cache.
The #CacheEvict is used at method level.
The #Cacheable annotation sets the value in cache and on the contrary #CacheEvict evicts the cache.
At one method we can use #Cacheable to cache result and at another method we can use #CacheEvict to evict cache.
The #CacheEvict annotation is introduced in Spring 3.1.
The #CacheEvict has following attributes.
String[] cacheNames
Cache names to evict.
String[] value
Alias for cacheNames.
String key
SpEL expression for computing the key dynamically.
String keyGenerator
The bean name of the custom KeyGenerator to use.
String cacheManager
The bean name of the custom CacheManager. It is used to create default CacheResolver if none is set already.
String cacheResolver
The bean name of the custom CacheResolver to use.
String condition
SpEL expression used for making the cache eviction operation conditional.
boolean allEntries
If true, all the entries inside the cache are removed.
boolean beforeInvocation
If true, the cache eviction will occur before the method is invoked.
I think you take the risk to never be satisfied by a solution crafted inside your application code to solve an architectural design problematic of the deployment of the application.
If you need to control the behavior of a variable set of nodes, you better have to design a small independent tool (e.g. in the form of a micro-service) that will receive "meta" requests and do the right thing: calling #CacheEvict and then #Cache entry points of all nodes in the right order. The list of nodes and the list of entry points to be called could be easily defined in parameter files or in a datasource.
You can also make your pods listening to a stream of events or an AMQP broadcaster.

Is it possible to #CacheEvict keys that match a pattern?

Is there something along the lines of #CacheEvict(value = "FOO", key = "baz*") so that when the cache FOO contains keys baz_1 and baz_2 they get evicted?
Assuming that you have spring-boot-starter-cache as dependency, spring boot auto-configures a CacheManager bean named cacheManager.
Also, assuming you have spring-boot-starter-data-redis as a dependency, RedisCacheManager is picked as the CacheManager implementation.
#CacheEvict (and the caching abstraction API) doesn't let you the option to evict by prefix, but using an AOP advice (or elsewhere where fits), you can take advantage of the underlying implementation:
RedisCache redisCache = (RedisCache) cacheManager.getCache("FOO");
redisCache.getNativeCache().clean("FOO", "baz*".getBytes());
Didn't try it actually, but I think this should work.
Likewise, you can adapt to other caching implementation.
The shortcoming of this approach, is that you'll have to change your code upon changing the cache implementation.

EhCache: #CacheEvict on Multiple Objects Using Annotations

I understand that using Spring's (3.1) built in CacheManager using the EhCache implementation, there are certain limitations when in proxy mode (the default) as per this post:
Spring 3.1 #Cacheable - method still executed
Consider the scenario I have:
#CacheEvict(value = "tacos", key = "#tacoId", beforeInvocation = true)
removeTaco(String tacoId) {
// Code to remove taco
}
removeTacos(Set<String> tacoIds) {
for (String tacoId : tacoIds) {
removeTaco(tacoId);
}
}
In this repository method, calling removeTacos(tacoIds) will not actually Evict anything from the Cache because of the limitation described above. My workaround, is that on a service layer above, if I wanted to delete multiple tacos, I'd be looping through each taco Id and passing it into removeTaco(), and never using removeTacos()
However, I'm wondering if there's another way to accomplish this.
1) Is there an SpEL expression that I could pass into the key that would tell EhCache to expire every id in the Set?
e.g. #CacheEvict(value = "tacos", key = "#ids.?[*]") // I know this isn't valid, just can't find the expression.
Or is there a way I can have removeTacos() call removeTaco and actually expire the Cached objects?
The #Caching annotation can be used to combine multiple annotations of the same type such as #CacheEvict or #CachePut, this is the example from the Spring documentation
#Caching(evict = { #CacheEvict("primary"), #CacheEvict(value="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)
You can do one of two things
#CacheEvict(value = "tacos", allEntries = true)
removeTacos(Set<String> tacoIds)
which is not so bad if tacos are read a lot more than they are removed
OR
removeTacos(Set<String> tacoIds) {
for (String tacoId : tacoIds) {
getTacoService().removeTaco(tacoId);
}
}
by calling the service (proxy) you invoke the cache eviction.
AFAIK #CacheEvict supports only removing single entry (by key) or all entries in given cache, there's no way to remove at once multiple entries. If you want to put, update or remove multiple objects from cache (using annotations) and you may switch to memcached take a look at my project Simple Spring Memcached (SSM).
Self invocations don't go through the proxy so one of the solution is to switch to other mode than proxy. Anther solution (I'm not recommending it) may be keeping reference to the service in service (as an autowired field) and use it to invoke removeTaco.
Several months ago I had similar issue in one of my projects. It didn't use Spring Cache but SSM which also requires proxy. To made it work I moved caching (annotations) from service to DAO (repositories) layer. It solved problem with self invocation.

unable to inject seam cache provider

Env: Seam 2.2, ehcache-core 2.1.0
I tried injecting the CacheProvider using the following call in my bean scoped for session
#In CacheProvider cacheProvider;
WEB-INF\components.xml contains the following line to enable the cache provider
<cache:eh-cache-provider/>
The above configuration seems to return a null value for the cache provider
Using the cache provider like this
CacheProvider cacheProvider = CacheProvider.instance();
throws the following warning
15:29:27,586 WARN [CacheManager] Creating a new instance of CacheManager using
the diskStorePath "C:\DOCUME~1\user5\LOCALS~1\Temp\" which is already used by an
existing CacheManager.
The source of the configuration was net.sf.ehcache.config.generator.Configuratio
nSource$DefaultConfigurationSource#15ed0f9.
The diskStore path for this CacheManager will be set to C:\DOCUME~1\user5\LOCALS
~1\Temp\\ehcache_auto_created_1276682367586.
To avoid this warning consider using the CacheManager factory methods to create
a singleton CacheManager or specifying a separate ehcache configuration (ehcache
.xml) for each CacheManager instance.
What am I missing here?
Keep in mind net.sf.ehcache.Cache needs to be on the classpath (I am not sure but I Think ehcache-core.jar contains this class) if you want to use EhCahceProvider. Here goes its signature
#Name("org.jboss.seam.cache.cacheProvider")
#Scope(APPLICATION)
#BypassInterceptors
#Install(value = false, precedence=BUILT_IN, classDependencies="net.sf.ehcache.Cache")
#AutoCreate
public class EhCacheProvider extends CacheProvider<CacheManager> {
Notice classDependencies attribute. Its documentation is clear
Indicates that the component should not be installed unless the the given class definitions are available on the classpath
So if your classpath contains net.sf.ehcache.Cache you do not need to declare
<cache:eh-cache-provider/>
And as it is Application scoped, you can retrieve, besides #In-jection, by using
ApplicationContext.getContext().get("cacheProvider");
UPDATE
First of all
remove <cache:eh-cache-provider/> declaration. I said you why (see above)
Second of all
Although i am pretty sure CacheProvider can not be null because #In required attribute is, by default, true, which cannot be null. Inside your business method, Make sure your CacheProvider is not null
assert cacheProvider != null
Third of all
I Think you do not need to call cacheProvider.instance() method. If its default scope is Application. Why do you want to retrieve another CacheProvider ??? It does not make sense.
Fourth of all
It is not an exception. Its is just a warning message because you is trying to use more than one cache provider where both use the same space in memory

Resources