31

I have a util module that produces a jar to be used in other applications. I'd like this module to use caching and would prefer to use Spring's annotation-driven caching.

So Util-Module would have something like this:


DataManager.java

...
@Cacheable(cacheName="getDataCache")
public DataObject getData(String key) { ... }
...

data-manager-ehcache.xml

...
<cache name="getDataCache" maxElementsInMemory="100" eternal="true" />
...

data-manager-spring-config.xml

...
<cache:annotation-driven cache-manager="data-manager-cacheManager" />
<!-- ???? --->
<bean id="data-manager-cacheManager" 
    class="org.springframework.cache.ehcache.EhcacheCacheManager" 
    p:cache-manager="data-manager-ehcache"/>
<bean id="data-manager-ehcache" 
    class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" 
    p:config-location="data-manager-ehcache.xml"/>
...

I would also like my deployable unit to have caching via Spring annotation, while including the above jar as a dependency. So my Deployable-Unit would have something like this:


MyApp.java

...
@Cacheable(cacheName="getMyAppObjectCache")
public MyAppObject getMyAppObject(String key) { ... }
...

my-app-ehcache.xml

...
<cache name="getMyAppObjectCache" maxElementsInMemory="100" eternal="true" />
...

my-app-spring-config.xml

...
<cache:annotation-driven cache-manager="my-app-cacheManager" />
<!-- ???? --->
<bean id="my-app-cacheManager" 
    class="org.springframework.cache.ehcache.EhcacheCacheManager" 
    p:cache-manager="my-app-ehcache"/>
<bean id="my-app-ehcache" 
    class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" 
    p:config-location="my-app-ehcache.xml"/>
...

Question:

Is it possible to use annotation driven caching in both your main project and a dependency module while keeping the configurations separated?

If not, an explanation of why it isn't would be appreciated. If so, an explanation of what needs to change in the above configuration would be appreciated.

Donal Fellows
  • 120,022
  • 18
  • 134
  • 199
Snekse
  • 14,360
  • 10
  • 53
  • 73
  • Hmm, 4 people up voted this question, but only 1 person has up voted the only answer here. It makes me think there is a better answer waiting to be discovered. – Snekse Jan 26 '12 at 01:47

5 Answers5

13

Use this class: http://static.springsource.org/autorepo/docs/spring/3.2.0.M1/api/org/springframework/cache/support/CompositeCacheManager.html like this:

<cache:annotation-driven cache-manager="cacheManager" />

<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
    <property name="cacheManagers">
        <array>
            <ref bean="cacheManager1" />
            <ref bean="cacheManager2" />
        </array>
    </property>
    <property name="addNoOpCache" value="true" />
</bean>
Nicolas Mommaerts
  • 2,957
  • 3
  • 30
  • 52
  • Since this matches the answer supplied by @user1187068, I edited that answer to include the information you provided to make a canonical answer to this question (which should hopefully appear soon). Thanks for the example code. – Snekse Feb 08 '12 at 16:48
9

this seems to be fixed in 3.2M1, see https://jira.springsource.org/browse/SPR-8696

bgranvea
  • 104
  • 3
  • I believe this is going to be the best solution going forward. As such, I've selected this as the new accepted answer. Thanks to @dfuse for the code example. – Snekse Feb 08 '12 at 16:49
  • Looks to have been ported to later versions of 3.1.x as well. http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/cache/support/CompositeCacheManager.html – Snekse Jan 08 '13 at 19:05
5

Spring currently expects the cacheManager to be a Singleton. This is something that the ehcache-spring-annotations project ran into and I've yet to see the request fulfilled. http://code.google.com/p/ehcache-spring-annotations/issues/detail?id=76

As with all things Java and Spring you do have the option to reimplement the class.

http://forums.terracotta.org/forums/posts/list/5618.page#27960 provides a base explanation of what some people have come up with as a workaround and

Is the actual code they came up with. The approach does create a convention to follow but it would be easy enough to reimplement this with your own version if you do not like the actual approach described.

John Leidegren
  • 56,169
  • 16
  • 118
  • 148
Joe
  • 1,003
  • 12
  • 19
  • 1
    After going through the source of the EhCacheManagerFactoryBean from Spring and the CacheManager from ehcache, I concur an approach similar to this is best. You can also just extend the EhCacheManagerFactoryBean and perform the configuration file combination there without doing a complete replacement like that on itcb. – aweigold Jan 23 '12 at 23:59
  • My concern with this solution is that I'd rather see each config get associated with it's own CacheManager rather than having the config files merged. I'm guessing this ItcbEhCacheManagerFactoryBean could be reworked so each config utilized a dedicated CacheManager internal to the FactoryBean, it just feels like a bit of a hack to me for some reason. – Snekse Jan 26 '12 at 02:02
  • I think this is an okay solution, but in light of a new answer pointing to upcoming changes in Spring 3.2M1, I'm changing the my accepted answer over to that method. – Snekse Feb 08 '12 at 16:47
2

In my Project I have been using ABC jar within XYZ war, both implementing ehCache with Spring 3.1,xml-driven configuration(we have ehCache.xml and then spring-context.xml where we are intercepting cache through Spring AOP in both the projects). And we are getting following error:

java.lang.IllegalArgumentException: Cannot find cache named [xxxxxx] for CacheableOperation[] caches=[Cxxxxxxxx] | condition='' | key='#xxxxxxxxxxxxx' 
at org.springframework.cache.interceptor.CacheAspectSupport.getCaches(CacheAspectSupport.java:163) [spring-context-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.<init>(CacheAspectSupport.java:443) [spring-context-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.getOperationContext(CacheAspectSupport.java:173) [spring-context-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.createOperationContext(CacheAspectSupport.java:404) [spring-context-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:192) [spring-context-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:66) [spring-context-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) [spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90) [spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) [spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622) [spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
    at com.infy.flypp.dao.ContentDAO$$EnhancerByCGLIB$$9443481.getContentById(<generated>) [cglib-2.2.2.jar:] 

Solution:

This is how we solved this issue:

  1. We copied all the cache configuration from ABCehCache.xml(from ABC jar) to XYZehCache.xml(from XYZ war).
  2. We deleted the ABCehCache.xml(from ABC jar) but all the configuration(like bean instantiation for ehCache.xml and Spring AOP) inside ABC-spring.xml will remain same.
  3. In XYZ-spring.xml, We imported ABC-spring.xml and defined composite cache manager.

Supported Configuration files:

ABC-spring.xml:

    <aop:aspectj-autoproxy proxy-target-class="true" />

    <bean id="CacheManager1" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehcache"></property>
    </bean>

    <bean id="ehcache"
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
        p:config-location="classpath:ABCEhcache.xml" />

XYZ-spring.xml:

<import resource="classpath*:ABC-spring.xml" />
<aop:aspectj-autoproxy proxy-target-class="true" />

    <bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
    <property name="cacheManagers">
        <array>
            <ref bean="CacheManager1" />
            <ref bean="CacheManager2" />
        </array>
    </property>
    <property name="fallbackToNoOpCache" value="true" />
</bean>

    <bean id="CacheManager2" class="org.springframework.cache.ehcache.EhCacheCacheManager"
        p:cache-manager-ref="ehcache" />
    <bean id="ehcache"
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
        p:config-location="classpath:XYZEhcache.xml" />
ritesh
  • 879
  • 3
  • 10
  • 31
1

I would consider the following much simpler alternatives:

  1. Option 1: Annotate your cacheable methods in the utility module with @Cacheable, but let the enclosing app create and configure the caches. In your example, you would declare and configure a cache 'getDataCache' in the app module, even though the cache is used in annotation to a class residing in the utility module.
  2. Option 2: let the utility module create the cache configuration, but not the cache manager itself. The app module would combine cache configurations from the utility module(s) and the app itself to create a single cache manager.

I did not like the solution of CompositeCacheManager, since its behavior is very dependent on the implementation of underlying caches: it will only work as expected if all underlying cache managers return null on unknown cache name. Some implementations would create them on the fly, resulting in caches with configuration that you did not expect.

Alexander
  • 2,367
  • 1
  • 22
  • 28
  • For the particular problem I was solving at the time, only option 2 would be viable because the util module needed to be responsible for the configuration. I'm also not sure if, at the time, I was able to separate the config from the manager - I think the annotation might complain if it can't find the manager. I'm not sure what the state of Spring caching is today - it's been awhile since I've used it. If you're able to do either of these, I agree, they are better options. – Snekse Nov 30 '15 at 17:46