I am using Spring Redis with the #Cacheable annotiation for two methods. When I call one method I am getting a result cached for the other method.
How can it happen that I get the result from the wrong cache while I configured a different cache for each method using the #Cachebale annotation?
Setup: Spring Version 4.1.6. Redis data 1.5 and Redis client 2.7.0.
Example code:
#Cacheable("test1")
public List<String> findSgsns() {
}
#Cacheable("test2")
public List<String> findSgsns2() {
}
The problem was sloved by adding following setting to spring configuration (set usePrefix):
<bean
id="cacheManager"
class="org.springframework.data.redis.cache.RedisCacheManager"
c:template-ref="redisTemplate">
<property name="usePrefix" value="true" />
</bean>
By default, Spring use SimpleKeyGenerator to generate the key if you don't specify it in the #Cacheable annotation.
public class SimpleKeyGenerator implements KeyGenerator {
#Override
public Object generate(Object target, Method method, Object... params)
{
return generateKey(params);
}
/**
* Generate a key based on the specified parameters.
*/
public static Object generateKey(Object... params) {
if (params.length == 0) {
return SimpleKey.EMPTY;
}
if (params.length == 1) {
Object param = params[0];
if (param != null && !param.getClass().isArray()) {
return param;
}
}
return new SimpleKey(params);
}
}
As there is no method arguments in both of your methods (findSgsns() and findSgsns2()), it essentially will generate the same cache key for both methods.
You've already found a solution which basically utilize usePrefix property in the redisTemplate bean, which essentially add you value ( namely, "test1" and "test2") you specified in your #Cacheable annotation when it forms the cache key in Redis. I would like to mention 2 more alternatives for the sake of completeness here:
Specify your own key for each method (Note: you can use Spring EL to specify your keys):
#Cacheable(value = "test1", key = "key1")
public List<String> findSgsns() {
}
#Cacheable(value = "test2", key = "key2")
public List<String> findSgsns2() {
}
Build a custom key generator, and below is sample key generator which takes method name into redis cache key generation (Note: the custom key generator will take effect automatically by extending CachingConfigurerSupport class):
#Configuration
public class RedisConfig extends CachingConfigurerSupport {
#Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
#Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
}
Related
I am using spring cache with Redis for caching
I have the following methods:
#CachePut(value ="DATA1", key = "#key1")
public Object saveData1(long key1, Object obj) {
return obj;
}
#CachePut(value ="DATA2", key = "#key1")
public Object saveData2(long key1, Object obj) {
return obj;
}
This is causing collisions in keys and the data is being overridden.
I want to generate the key with the cache name appended to it.
Like: DATA1-key1, DATA2-key1.
Is it possible?
I have seen a few examples which use class name and method name. But I want to use the cache name.
Thank you.
Create a custom key generator like this:
#Component("myKeyGenerator")
public class MyKeyGenerator implements KeyGenerator {
public Object generate(Object target, Method method, Object... params) {
String[] value = new String[1];
long key;
CachePut cachePut = method.getAnnotation(CachePut.class);
if (cachePut != null) {
value = cachePut.value();
}
key = (long) params[0];
return value[0] + "-" + key;
}
}
And use it like below:
#CachePut(value = "DATA1", keyGenerator = "myKeyGenerator")
I haven't test this but should work, atleast you will get a basic idea how to do it.
You need to set parameter "usePrefix" as true in your CacheManager bean. This will prepend cacheName in your keys.
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
...
<property name="usePrefix"><value>true</value></property>
...
</bean>
I have problems with save some values in #Service method.
My code:
#Service(value = "SettingsService")
public class SettingsService {
...
public String getGlobalSettingsValue(Settings setting) {
getTotalEhCacheSize();
if(!setting.getGlobal()){
throw new IllegalStateException(setting.name() + " is not global setting");
}
GlobalSettings globalSettings = globalSettingsRepository.findBySetting(setting);
if(globalSettings != null)
return globalSettings.getValue();
else
return getGlobalEnumValue(setting)
}
#Cacheable(value = "noTimeCache", key = "#setting.name()")
public String getGlobalEnumValue(Settings setting) {
return Settings.valueOf(setting.name()).getDefaultValue();
}
My repository class:
#Repository
public interface GlobalSettingsRepository extends CrudRepository<GlobalSettings, Settings> {
#Cacheable(value = "noTimeCache", key = "#setting.name()", unless="#result == null")
GlobalSettings findBySetting(Settings setting);
It should work like this:
get value form DB if data exist,
if not save value from enum.
but it didn't save any data from DB or enum.
My cache config:
#Configuration
#EnableCaching
public class CacheConfig {
#Bean
public EhCacheCacheManager cacheManager(CacheManager cm) {
return new EhCacheCacheManager(cm);
}
#Bean
public EhCacheManagerFactoryBean ehcache() {
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
return ehCacheManagerFactoryBean;
}
}
I have some example to make sure that cache is working in my project in rest method:
#RequestMapping(value = "/system/status", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> systemStatus() {
Object[] list = userPuzzleRepository.getAverageResponseByDateBetween(startDate, endDate);
...
}
public interface UserPuzzleRepository extends CrudRepository<UserPuzzle, Long> {
#Cacheable(value = "averageTimeAnswer", key = "#startDate")
#Query("select AVG(case when up.status='SUCCESS' OR up.status='FAILURE' OR up.status='TO_CHECK' then up.solvedTime else null end) from UserPuzzle up where up.solvedDate BETWEEN ?1 AND ?2")
Object[] getAverageResponseByDateBetween(Timestamp startDate, Timestamp endDate);
and it's work well.
What am I doing wwrong?
You have two methods in your SettingsService, one that is cached (getGlobalEnumValue(...)) and another one that isn't cached, but calls the other method (getGlobalSettingsValue(...)).
The way the Spring cache abstraction works however is by proxying your class (using Spring AOP). However, calls to methods within the same class will not call the proxied logic, but the direct business logic beneath. This means caching does not work if you're calling methods in the same bean.
So, if you're calling getGlobalSettingsValue(), it will not populate, nor use the cache when that method calls getGlobalEnumValue(...).
The possible solutions are:
Not calling another method in the same class when using proxies
Caching the other method as well
Using AspectJ rather than Spring AOP, which weaves the code directly into the byte code at compile time, rather than proxying the class. You can switch the mode by setting the #EnableCaching(mode = AdviceMode.ASPECTJ). However, you'll have to set up load time weaving as well.
Autowire the service into your service, and use that service rather than calling the method directly. By autowiring the service, you inject the proxy into your service.
The problem is in the place you call your cacheable method from. When you call your #Cacheable method from same class, you just call it from this reference, which means it doesn't wrapped by Spring's proxy, so Spring can't catch your invocation to handle it.
One on ways to solve this problem is to #Autowired service to itself and just call methods you expected spring have to handle by this reference:
#Service(value = "SettingsService")
public class SettingsService {
//...
#Autowired
private SettingsService settingsService;
//...
public String getGlobalSettingsValue(Settings setting) {
// ...
return settingsSerive.getGlobalEnumValue(setting)
//-----------------------^Look Here
}
#Cacheable(value = "noTimeCache", key = "#setting.name()")
public String getGlobalEnumValue(Settings setting) {
return Settings.valueOf(setting.name()).getDefaultValue();
}
}
But if you have such problems it means your classes are take on too much and aren't comply with the principle of "single class - single responsibility". The better solution would be to move method with #Cacheable to dedicated class.
I've Spring cache implemented as below
#Component
public class KPCacheExample {
private static final Logger LOG = LoggerFactory.getLogger(KPCacheExample.class);
#CachePut(value="kpCache")
public String saveCache(String userName, String password){
LOG.info("Called saveCache");
return userName;
}
#Cacheable(value="kpCache")
public String getCache(String userName, String password){
LOG.info("Called getCache");
return "kp";
}
}
And Java Config file
#Configuration
#ComponentScan(basePackages={"com.kp"})
public class GuavaCacheConfiguration {
#Bean
public CacheManager cacheManager() {
GuavaCacheManager guavaCacheManager = new GuavaCacheManager("kpCache");
guavaCacheManager.setCacheBuilder(CacheBuilder.newBuilder().expireAfterAccess(2000, TimeUnit.MILLISECONDS).removalListener(new KPRemovalListener()));
return guavaCacheManager;
}
}
By default the spring uses put method in the cache interface to update/put values in the cache. How can I force the spring to use putifabsent method to be invoked, such that I can get null value if cache is missed or in other wards first request to the method with unique username and password should return null and subsequent request to that username and password should return username.
Well, looking through Spring's Cache Abstraction source, there does not appear to be a configuration setting (switch) to default the #CachePut to use the "atomic" putIfAbsent operation.
You might be able to simulate the "putIfAbsent" using the unless (or condition) attribute(s) of the #CachePut annotation, something like (based on the Guava impl)...
#CachePut(value="Users", key="#user.name" unless="#root.caches[0].getIfPresent(#user.name) != null")
public User save(User user){
return userRepo.save(user);
}
Also note, I did not test this expression, and it would not be "atomic" or portable using a different Cache impl. The expression ("#root.caches[0].get(#user.name) != null") maybe more portable.
Giving up the "atomic" property may not be desirable so you could also extend the (Guava)CacheManager to return a "custom" Cache (based on GuavaCache) that overrides the put operation to delegate to "putIfAbsent" instead...
class CustomGuavaCache extends GuavaCache {
CustomGuavaCache(String name, com.google.common.cache.Cache<Object, Object> cache, boolean allowNullValues) {
super(name, cache, allowNullValues);
}
#Override
public void put(Object key, Object value) {
putIfAbsent(key, value);
}
}
See the GuavaCache class for more details.
Then...
class CustomGuavaCacheManager extends GuavaCacheManager {
#Override
protected Cache createGuavaCache(String name) {
return new CustomGuavaCache(name, createNativeGuavaCache(name), isAllowNullValues());
}
}
See GuavaCacheManager for further details, and specifically, have a look at line 93 and createGuavaCache(String name).
Hope this helps, or at least gives you some more ideas.
I would like to be able to choose specific Spring (or Grails) context configuration based on the tenant that user belongs to in runtime. Let's say I use Spring Security and I retrieve tenantId during login.
Imagine now I have a two tenants and they pay different commission. How to inject specific service into a controller without too much plumbing? Here are two different contexts. So, I should inject different ExchangeService based on tenant.
#Configuration
public class FooTenant{
#Bean
public ExchangeService bar() {
return new ZeroCommisionExchangeService ();
}
}
#Configuration
public class BarTenant{
#Bean
public ExchangeService bar() {
return new StandardCommisionExchangeService ();
}
}
Edit:
I am aware I can obtain reference to Spring context and ask for service "manually", but I am looking for a more generic solution where this problematic is solved by IoC framework.
A couple of years ago we needed somthing like this but only for DataSources and ViewResolvers. We developed a solution using spring' TargetSource solution. (Initially we used a HotswappableTargetSource but that wasn't adequate for our use-case.
The code we developed is availabe here in the multi-tenant directory.
It is fully configurable and flexible.
Basically what you do is you configura a ContextSwappableTargetSource and tell it what type of interface/class it needs to return.
<bean id="yourTentantBasedServiceId" class="biz.deinum.multitenant.aop.target.ContextSwappableTargetSource">
<constructor-arg value="ExchangeService" />
</bean>
The default is to lookup beans in the ApplicationContext based on the tenantId (see the BeanFactoryTargetRegistry for this). However you can specify one or more of those (we used a JndiLookupTargetRegistry to dynamically lookup datasource, which allowed use to add tenants on the fly without restarting the application).
If you explicitly configure a BeanFactoryTargetRegistry you can add a prefix and suffix.
<bean id="exchangeService" class="biz.deinum.multitenant.aop.target.ContextSwappableTargetSource">
<constructor-arg value="ExchangeService" />
<property name="targetRegistry>
<bean class="biz.deinum.multitenant.aop.target.registry.impl.BeanFactoryTargetRegistry">
<property suffix="ExchangeService"/>
</bean>
</property>
</bean>
Now for foo it would lookup a bean named fooExchangeService and for bar barExchangeService.
The tenantId is stored in a ThreadLocal which is wrapped inside the ContextHolder. You need to find a way to fill and clear this thread local (in general a servlet Filter does that trick.
In your code you can now simply use the interface ExchangeService and at runtime based on the tenantId the correct implemenation will be looked up.
Also see http://mdeinum.wordpress.com/2007/01/05/one-application-per-client-database/
Assuming you have different services already defined, you can get their bean from the context and use it. In my example, all the services have implementation of serviceMethod and based on some criteria pick your proper service. The only thing I am not sure is how Multitenancy might impact this.
import org.springframework.context.ApplicationContext
class ServiceManagerController {
def serviceManager
def index() {
ApplicationContext ctx = grails.util.Holders.grailsApplication.mainContext
serviceManager = ctx.getBean(params.serviceName); //firstService or secondService
render serviceManager.serviceMethod()
}
}
FirstService
class FirstService {
def serviceMethod() {
return "first"
}
}
SecondService:
class SecondService {
def serviceMethod() {
return "second"
}
}
While it is possible to swap beans instantiated in a spring context at runtime (HotswappableTargetSource), it is not meant for use cases such as yours.
Remember there is one Spring Context for your application, all threads use the same instances (in most cases), this implies when you swap out a bean implementation, you are affectively doing this for all your application's users. To prevent this, you run into issues of ensuring Thread Safety, employing Thread Locals, as listed in another answer.
While it is possible to continue this approach and arrive at an implementation that gets the job done, it would definitely be a very contrived way of solving this problem.
You should take a step back and look at your problem in a more wholesome, system wide design point of view. Bust out your patterns books and look at how this can be resolved, regardless of whether you use Spring or an other framework. Service Locator, Factory bean etc described in some of the answers above is a step in the correct direction.
Your Use Case is pretty common for multi-tenant applications. You need to narrow down things that are likely to change based on a tenantId versus things that are constant across.
For instance as mentioned in the question, each Tenant might have a different commission amount or even different algorithm for commission calculation. A simple solution to this would be to implement a CommissionCalculationService which accepts a tenantId, and any other domain object based on which commission is to be calculated, I would imagine this would be something like Order or Sale, whatever makes sense in your application.
You now need a CommissionServiceFactory or a ServiceLocator which will contain tenant specific implementations of the CommissionCalculationService. The Service Locator is instantiated when the Spring context loads, and is injected with implementation classes also at application startup.
When you want to calculate commission for a tenant, you basically obtain the tenantId from the user's login, pass the tenant id to your service locator, based on the tenantId passed, the service locator returns the appropriate instance of a Service Implementation. In your calling class, use this instance to calculate the commission for the tenant.
Another pattern to consider is the Strategy Pattern, or even Template Pattern.
Bottom line, even if you want tenant specific logic implemented cleanly, don't thing about changing the beans loaded in the context. Have classes in your context that can handle all your tenant specific logic. Rely on design patterns to use the correct bean from the context based on the tenant id.
I apologize if the answer was a little verbose, I felt it was needed to explain why I think updating beans in a loaded Spring Context is not the appropriate solution.
I use the following code:
public class ConfigurableProxyFactoryBean implements FactoryBean<Object>, BeanNameAware {
#Autowired
private ApplicationContextProvider applicationContextProvider;
private Class<?> proxyType;
private String beanName;
private Object object;
private Object fallbackObject;
private Object monitor = new Object();
private ConfigurableProxy proxy;
public ConfigurableProxyFactoryBean(Class<?> proxyType) {
this.proxyType = proxyType;
}
public Object getFallbackObject() {
return fallbackObject;
}
public void setFallbackObject(Object fallbackObject) {
synchronized (monitor) {
this.fallbackObject = fallbackObject;
if (proxy != null) {
proxy.setFallbackObject(fallbackObject);
}
}
}
#Override
public void setBeanName(String name) {
beanName = name;
}
#Override
public Object getObject() throws Exception {
synchronized (monitor) {
if (object == null) {
#SuppressWarnings("unchecked")
Class<Object> type = (Class<Object>)proxyType;
proxy = new ConfigurableProxy(applicationContextProvider, beanName);
proxy.setFallbackObject(fallbackObject);
object = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[] { type }, proxy);
}
return object;
}
}
#Override
public Class<?> getObjectType() {
return proxyType;
}
#Override
public boolean isSingleton() {
return true;
}
}
class ConfigurableProxy implements InvocationHandler {
public ConfigurableProxy(ApplicationContextProvider appContextProvider, String beanName) {
this.appContextProvider = appContextProvider;
this.beanName = beanName;
}
private ApplicationContextProvider appContextProvider;
private String beanName;
private Object fallbackObject;
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ApplicationContext appContext = appContextProvider.getApplicationContext();
String name = "$&&#" + beanName;
Object bean = appContext.containsBean(name) ? appContext.getBean(name) : fallbackObject;
return method.invoke(bean, args);
}
public void setFallbackObject(Object fallbackObject) {
this.fallbackObject = fallbackObject;
}
}
ApplicationContextProvider has implementation, that chooses ApplicationContext according to current tennant.
In XML configuration it is used like this:
<bean class="my.package.infrastructure.ConfigurableProxyFactoryBean" name="beanName">
<constructor-arg>
<value type="java.lang.Class">my.package.model.ServiceInterface</value>
</constructor-arg>
<property name="fallbackObject">
<bean class="my.package.service.DefaultServiceImplementation"/>
</property>
</bean>
And in tennant configuration that way:
<bean class="my.package.service.ServiceImplementationA" name="$&&#beanName"/>
To inject this service somewhere you just write:
public class MyController {
#Autowired
private ServiceInterface service;
}
Also you are to implement ApplicationContextProvider, I won't share mine. It is not very hard to implement. For example, your implementation can just store context in ThreadLocal. And you create your own ServletContextListener, which for every query gets the current tennant and stores it into your ApplicationContextProvider implementation.
The new tenant scope and a servicelocator can helps
Tenant scope will guarantee than service is created one time for a tenant
Sample code:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="tenant" value="foo.TenantScope"/>
</map>
</property>
</bean>
<bean id="service" class="foo.Service" factory-bean="tenantServiceLocator" factory-method="createInstance" scope="tenant"/>
<bean id="fooService" class="FooService">
<bean id="barService" class="BarService">
<bean id="tenantServiceLocator" class="foo.TenantServiceLocator">
<property name="services">
<map>
<entry key="foo" value-ref="fooService"/>
<entry key="bar" value-ref="barService"/>
</map>
</property>
</bean>
TenantServiceLocator should know the user tenantId
public class TenantServiceLocator {
private Map<String, Service> services;
public String getTenantId() {
return "foo"; // get it from user in session
}
public Map<String, Service> getServices() {
return services;
}
public void setServices(Map<String, Service> services) {
this.services = services;
}
public Service createInstance(){
return services.get(tenantId);
}
}
public class FooController{
#Autowired
private Service service;
}
A sample TenantScope implementation
public class TenantScope implements Scope {
private static Map<String, Map<String, Object>> scopeMap = new HashMap<String, Map<String, Object>>();
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = getTenantScope(getTenantId());
Object object = scope.get(name);
if(object == null){
object = objectFactory.getObject();
scope.put(name, object);
}
return object;
}
private Map<String, Object> getTenantScope(String tenantId) {
if (!scopeMap.containsKey(tenantId)) {
scopeMap.put(tenantId, new HashMap<String, Object>());
}
return scopeMap.get(tenantId);
}
private String getTenantId() {
return "foo"; // load you tenantId
}
#Override
public Object remove(String name) {
Map<String, Object> scope = getTenantScope(getTenantId());
return scope.remove(name);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
}
#Override
public Object resolveContextualObject(String key) {
return null;
}
#Override
public String getConversationId() {
return null;
}
}
Transforming my comment in an answer, one possible solution is to create a spring factory bean, that receive all he needs to decide which service needs to be returned when creating the instance.
Translating to Grails:
public interface ChoosableServiceIntf {
String getName();
}
class NormalService implements ChoosableServiceIntf {
public String getName() {
return getClass().name;
}
}
class ExtendedService implements ChoosableServiceIntf {
public String getName() {
return getClass().name
}
}
class ChoosableServiceFactory {
static ChoosableServiceIntf getInstance(String decisionParam) {
if(decisionParam == 'X') {
return applicationContext.getBean('extendedService')
}
return applicationContext.getBean('normalService')
}
static ApplicationContext getApplicationContext() {
return Holders.grailsApplication.mainContext
}
}
Here we have two services and ChoosableServiceFactory is responsible to know witch is the correct one.
Then you will need to use the method ApplicationContext#getBean(String, Object[]) to return the correct instance and will also make the factory prototyped scope because of the runtime params.
A controller to test it:
class MyController {
def grailsApplication
def index() {
ChoosableServiceIntf service = grailsApplication.mainContext.getBean('choosableServiceFactory', ["X"] as Object[])
ChoosableServiceIntf serviceNormal = grailsApplication.mainContext.getBean('choosableServiceFactory', ["N"] as Object[])
render text: "#1 - ${service.class.name} , #2 - ${serviceNormal.class.name}"
}
}
This will print #1 - dummy.ExtendedService , #2 - dummy.NormalService
The declaration of the beans will be:
choosableServiceFactory(ChoosableServiceFactory) { bean ->
bean.scope = 'prototype'
bean.factoryMethod = 'getInstance'
}
normalService(NormalService)
extendedService(ExtendedService)
I've used spring declarative annotation based caching approach. Here is how I've used it,
#Cacheable(value = "users", key = "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getName().concat(#username)")
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException, DataAccessException { //method body }
This annotation looks very lengthy. I've tried to use a custom key-generator but the issue is when I define a key in the annotation,the custom key generator not get invoked.
So now I am trying to use a custom spring annotation as a workaround. But I was unable to find a good reference to start off. Basically I need to add a context aware parameter to the key (the tenant Identifier).
Any help will be greatly appreciated.
Key has to be a static variable. It cannot be a runtime evaluation.
You need to override the CacheManager and then do the modification. Below is an example where I extend HazelcastCacheManager which in turn extends spring's Cachemanager
public class MyCache extends HazelcastCacheManager {
private final ConcurrentMap<String, Cache> myCaches = new ConcurrentHashMap<String, Cache>();
public MyCache(){
super();
}
public MyCache(HazelcastInstance hazelcastInstance){
super(hazelcastInstance);
}
#Override
public Cache getCache(String name) {
String tenant = org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getName();
Cache cache = myCaches.get(tenant.concat("#").concat(name));
if (cache == null) {
IMap<Object, Object> map = getHazelcastInstance().getMap(tenant.concat("#").concat(name));
cache = new HazelcastCache(map);
Cache currentCache = cesCaches.putIfAbsent(tenant.concat("#").concat(name), cache);
if (currentCache != null) {
cache = currentCache;
}
}
return (Cache)cache;
}