I have a method that fetches all the data and i am caching the result of that method but i am not able to evict the result.
#Component("cacheKeyGenerator")
public class CacheKeyGenerator implements KeyGenerator {
#Override
public Object generate(Object target, Method method, Object... params) {
final List<Object> key = new ArrayList<>();
key.add(method.getDeclaringClass().getName());
return key;
}
}
CachedMethod:-
#Override
#Cacheable(value="appCache",keyGenerator="cacheKeyGenerator")
public List<Contact> showAllContacts() {
return contactRepository.findAll();
}
#Override
#CachePut(value="appCache",key="#result.id")
public Contact addData(Contact contact) {
return contactRepository.save(contact);
}
Now when ever addData is called i want the data in the cache "appCache" with the key ="cacheKeyGenerator" to be evicted.So that the data returned by the method "showAllContacts()" is accurate.Can anyone please help!
The Entire code can be found at - https://github.com/iftekharkhan09/SpringCaching
Assuming you have a known constant cache key for showAllContacts then the solution should be to simply add #CacheEvict on addData passing in the cache name and key value:
#Override
#Caching(
put = {#CachePut(value="appCache", key="#result.id")},
evict = {#CacheEvict(cacheNames="appCache", key="someConstant")}
)
public Contact addData(Contact contact) {
return contactRepository.save(contact);
}
However because you use a key generator it is a bit more involved. Now given what your key generator does, you could instead pick a value for that cache key, making sure there can't be any collisions with the values from #result.id and use that value instead of a the key generator returned one.
Related
I just want to disable cache for users that are admins. So I write a method to generate keys as below that returns null for admins. But I get
java.lang.IllegalArgumentException: Null key returned for cache
operation
exeption.
Is there any way achieve that?
//a method that generates a menu for each user
#Cacheable(cacheNames = "topmenu", keyGenerator = "uiComponentKey")
#Override
public String renderResponse() {...}
//method used by a key generator to generate cache keys.
#Override
public Object getCacheKey() {
if (user.isAdmin()) {
return null;
}
return user.getUser().getLogin() + "#" + "topmenu";
}
I guess you can achive that using conditional caching feature. Smth like this:
#Cacheable(cacheNames = "topmenu", condition="#user.isAdmin()")
#Override
public String renderResponse(User user) {...}
Note, that you're going to have to pass user object to this method in this case.
I am caching some object using Spring cache implementation, the underline cache is EhCache. I am trying to evict the cache based on wildcard search for the keys,the reason is the way I stored them and I only know the partial key. Hence I wanted to do something like below. I did search this forum for relevant answer but did not find any.
#CacheEvict(beforeInvocation=true, key="userId+%")
public User getUser(String userId)
{
//some implementation
}
Now if I try this I get an error for the SPEL. Also I tried to create a custom keygenerator for this, here the eviction works if the key generator returns one key, but I have a couple of keys based on my search.
#CacheEvict(beforeInvocation=true, keyGenerator="cacheKeyEvictor")
public User getUser(String userId)
{
//some implementation
}
//Custom key generator for eviction
public class cacheKeyEvictor implements KeyGenerator {
#Override
public Object generate(Object arg0, Method arg1, Object... arg2) {
//loop the cache and do a like search and return the keys
return object; //works if I send one key. Won't work for a list of keys
}
}
Any help on this is appreciated.
I am wondering what is the purpose of org.springframework.beans.factory.config.Scope.resolveContextualObject(String key) and org.springframework.beans.factory.config.Scope.getConversationId()?
From the javadoc:
Object resolveContextualObject(String key)
Resolve the contextual object for the given key, if any. E.g. the HttpServletRequest object for key "request".
String getConversationId()
Return the conversation ID for the current underlying scope, if any.
The exact meaning of the conversation ID depends on the underlying storage mechanism. In the case of session-scoped objects, the conversation ID would typically be equal to (or derived from) the session ID; in the case of a custom conversation that sits within the overall session, the specific ID for the current conversation would be appropriate.
This description doesn't tell me much.
Could you give me some examples which demonstrate how to make use of these methods?
My observation is that resolveContextualObject(String key) looks like a code smell, where where a Scope can expose some internal object.
Having:
public class MyCustomScope implements Scope {
private Pair<String, String> myPair;
#Override
public Object resolveContextualObject(String key) {
if ("myKey".equals(key)) return myPair;
return null;
}
// ...
}
#Configuration
public class RegisterMyScopeConfig {
#Bean
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope(
"mycustomscope", new MyCustomScope());
}
}
Then you can:
#Scope("mycustomscope")
#Component
class MyComponent {
#Value("#{myKey.first}")
private String firstOfMyPair;
// or
#Value("#{myKey}")
private Pair<String,String> myPair;
}
Of course the way how you resolved object which matches key, might be fancier ;).
For example, in GenericScope it looks like that:
#Override
public Object resolveContextualObject(String key) {
Expression expression = parseExpression(key);
return expression.getValue(this.evaluationContext, this.beanFactory);
}
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 need to pass some field values from one jsp to another jsp using Struts2 and action classes. Can any one suggest me the best way to do it. How to pass values using SessionAware interface?
Implement SessionAware interface and unimplemented methods. After this you just need to add parameter in Map. The Map will contain all session variable vales as Key value pair. you can add, remove values from Map.
Here is a Example of Action Class
public class SampleForm implements SessionAware{
//Fields that hold data
private String Welcome1="";
// This Map will contain vales in Session
private Map session;
public String execute() throws Exception {
return SUCCESS;
}
public void setWelcome1(String s) {
this.Welcome1= s;
}
public String getWelcome1() {
return Welcome1;
}
public void setSession(Map session) {
this.session = session;
}
public Map getSession() {
return session;
}
}
If you implement SessionAware then your actions will receive a Map containing the session variables. If one action puts a value into the map:
session.put("username", "Newbie");
then later actions can retrieve that value from the map:
String username = session.get("username");