7

I'm using Spring Boot and EhCache to develop a calendar application. I'm trying to cache the following method:

@Override
@Cacheable(value = "concerts")
public List<Event> getEvents(String eventsForUser, Date startDate, Date endDate) throws Exception {
    return fetchEventsFromTheServer(eventsForUser, startDate, endDate);
}

The challenge is I would like to manipulate returned cached result. For example, check if there is cache for given dates but for a different user and then return it instead (as long as both users meet certain criteria).

So, before returning a result I would like:

  • to get a list of all cached entries
  • loop through all of them and check for needed dates/users
  • if found suitable - return that
  • if not found - cache is not available, run the method.

I think the best would be to create a custom Cache Manager which will do all the manipulation with cached concert and use default auto configured cache for all other methods, but I can't get my custom manager to work and didn't find any good example on how to implement a custom CacheManager.

Here is what I have:

Application.java:

@SpringBootApplication
@ComponentScan
@EnableCaching
@EnableScheduling
public class SpringBootWebApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootWebApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(applicationClass);
    }

    private static Class<SpringBootWebApplication> applicationClass = SpringBootWebApplication.class;


    @Bean(name = "eventsCacheManager")
    public EventsCacheManager eventsCacheManager() {

        return new EventsCacheManager();
    }

    @Primary
    @Bean(name = "cacheManager")
    public CacheManager cacheManager() {
        return new EhCacheCacheManager(ehCacheCacheManager().getObject());
    }

    @Bean
    public EhCacheManagerFactoryBean ehCacheCacheManager() {
        EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
        cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));
        cmfb.setShared(true);
        return cmfb;
    }


}

EventsCacheManager.java

@Component
public class EventsCacheManager implements CacheManager  {

    @Override
    public Cache getCache(String s) {
        return null;
    }

    @Override
    public Collection<String> getCacheNames() {
        return null;
    }
}

EventsCacheManager.java - how to implement it?

@Component
public class EventsCacheManager implements CacheManager  {

    @Override
    public Cache getCache(String s) {
        return null;
    }

    @Override
    public Collection<String> getCacheNames() {
        return null;
    }
}

I would really appreciate if someone can give me an example on how I should implement my custom CacheManager

5
  • If you check the result afterwards, using a cache is pretty pointless as you still have the performance hit of querying the database. You want to check before the method is executed. IMHO what you need is only a custom KeyGenerator instead of a full blown custom cache. Which does the check before you execute the method. Commented Jun 13, 2016 at 8:06
  • Unfortunately, I won't be able to achieve this with KeyGenerator alone. There are some other manipulations I need to do with the result (like, remove some concerts based on its date if users don't match completely etc.), so the only solution is to subclass a cache manager and manipulate the date before returning it. Commented Jun 13, 2016 at 13:24
  • Then it doesn't really make sense to use a cache, as you basically loose the benefits of a cache. Commented Jun 13, 2016 at 13:57
  • I understand, but the 3rd party server I'm fetching the data from is extremely slow, so I want to minimize the number of requests I'm sending to it and instead use the data that is already cached on my server. Instead of making a new request I want to try to extract a subset of cached events I already have from before. Commented Jun 13, 2016 at 14:35
  • What i also want to do is to fetch concerts for the entire month and don't make a new lookup when user requests concerts for a specific day within the same month, since day-overview can be extracted from the month-overview Commented Jun 13, 2016 at 14:41

2 Answers 2

2

I did not spend much time thinking about your requirements/use case, but I do think a custom CacheManager would work in this situation, assuming the "custom" CacheManager logic is correct.

So, by default, Spring looks for a bean in the context with the name "cacheManager" and uses it for all cached operations. In your configuration, you clearly have 2 "CacheManagers" defined...

@Bean(name = "eventsCacheManager")
public EventsCacheManager eventsCacheManager() {

    return new EventsCacheManager();
}

@Primary
@Bean(name = "cacheManager")
public CacheManager cacheManager() {
    return new EhCacheCacheManager(ehCacheCacheManager().getObject());
}

I.e. the "eventsCacheManager" (custom) and "cacheManager" (standard). Indeed, you even marked the "cacheManager" as primary (using the @Primary annotation). Of course, had you not done that, Spring would certainly have complained since more than 1 bean of a given type (i.e. CacheManager) was found in the context when performing auto-wiring (which defaults to auto-wire by type).

So, in order to call out which cache management strategy (i.e. CacheManager) to use at runtime with a particular service call, you also need to tell Spring which CacheManager (aka strategy) to use, like so...

@Override
@Cacheable(value = "concerts", cacheManager="eventsCacheManager")
public List<Event> getEvents(String eventsForUser, Date startDate, Date endDate) throws Exception {
    return fetchEventsFromTheServer(eventsForUser, startDate, endDate);
}

I.e. using the cacheManager attribute in the @Cacheable annotation to indicate which caching strategy to use.

See Spring's Reference Doc on Custom cache resolution for more details.

Hope this helps get you going.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for your response, actually my problem is on how to implement custom cache manager? Please see my 'EventsCacheManager' snippet. Those two methods get called no problem, but I don't know what to write there so they return something...
Right, I see. I too echo what @Louis Jacomet says as it is a more proactive and sensible approach to caching. However, it is a simple matter to "extend" the Spring CacheManager implementation along with return Cache abstractions, that actually do the work of interfacing with the underlying caching provider. I have built several examples in the past pertaining to various questions on SO. I will add additional comments with some examples.
Here is an example of handling a collection of values cached individually in the Spring's Cache abstraction (uses custom Spring CacheManager and Cache implementations).. github.com/jxblum/spring-gemfire-tests/blob/master/src/test/…
Hmm, well I thought I had more examples (github.com/jxblum/spring-gemfire-tests/tree/master/src/test/…), but perhaps not.
At any rate, I would question the need to extend the CacheManager, potentially returning a custom Cache implementation in all scenarios, and perhaps this one in particular, but to answer your questions... getCache(..) returns a Cache representation "adapting" the underlying cache data structure (e.g. in GemFire, it is a Region) and getCacheNames() returns a list of all the "caches" (by name, of course) managed by the CacheManager. In come cases (e.g. ConcurrentCacheManager) a Cache can be created dynamically. In other cases...
|
1

From my understanding of your question, I believe you are looking at the problem the wrong way.

Instead of having to browse cache contents to extract derived information, you should insert into the cache the derived information at the time of loading the main data.

For example, when loading month based information, break it immediately into day based information and put that in a cache as well.

This description also should clearly indicate that what you want to do is beyond the capabilities of the Spring caching abstraction, as you need custom cache loading logic.

So I would not recommend hacking a CacheManager to hide that logic but instead do it from the data loading logic.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.