Announcement Announcement Module
Collapse
No announcement yet.
Cache never populated with @Cacheable Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Cache never populated with @Cacheable

    Hi,

    I'm trying to apply caching on my DAO methods for a REST Web application
    I have a simple configuration and I am using Spring 3.2, EhCache-core 2.6.3, Hibernate 4 and MySQL.
    i've tried Tomcat 6 and Tomcat 7 as servletcontainer to run the webapp.

    api-servlet.xml:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans ...">
    	<context:component-scan base-package="nl.bla.api." >
    		<context:include-filter type="aspectj" expression="nl.bla.api..controller"/>
    	</context:component-scan>
    	<mvc:annotation-driven >
    		<mvc:argument-resolvers>
    			<bean class="nl.kerken.api.web.support.RequestEntityMethodArgumentResolver" />
    		</mvc:argument-resolvers>
    	</mvc:annotation-driven>
    	<mvc:interceptors>
    		<bean class="org.springframework.orm.hibernate4.support.OpenSessionInViewInterceptor">
    			<property name="sessionFactory" ref="sessionFactory" />
    		</bean>
    	</mvc:interceptors>
    	
    </beans>
    context.xml

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans ...>
    	<context:component-scan base-package="nl.bla.api" >
     		<context:include-filter type="aspectj" expression="nl.bla.api..dao"/>
     		<context:include-filter type="aspectj" expression="nl.bla.api.location"/>
    	</context:component-scan>
    	<cache:annotation-driven/>
    	<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
    		<property name="cacheManager" ref="ehcache" />
    	</bean>
    	<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    		<property name="configLocation" value="classpath:ehcache.xml"/>
    	</bean>
    
    </beans>
    database.xml:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans ...>
    
    	<!-- enable @transactional scanning -->
    	<tx:annotation-driven/>
    
    	<!-- datasource -->
    	<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    		<property name="driverClass" value="com.mysql.jdbc.Driver"/>
    		<property name="jdbcUrl" value="${jdbc.url}"/>
    		<property name="user" value="${jdbc.username}"/>
    		<property name="password" value="${jdbc.password}"/>			
    		<property name="minPoolSize" value="1"/>
    		<property name="maxPoolSize" value="5"/>
    		<property name="idleConnectionTestPeriod" value="360"/>
    	</bean>
    
    	<!-- hibernate sessionFactory -->
    	<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    		<property name="dataSource" ref="datasource" />
    		<property name="packagesToScan" value="nl.bla.api.domain" />
    		<property name="hibernateProperties">
    			<props>
    				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    				<prop key="hibernate.connection.zeroDateTimeBehavior">convertToNull</prop>
    			</props>
    		</property>
    	</bean>
    	
    	<!-- hibernate transactionmanager -->
    	<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    		<property name="sessionFactory" ref="sessionFactory" />
    	</bean>
    	
    	
    	<beans profile="test">
    		<context:property-placeholder location="<some_props_location>"/>
    	</beans>
    	
    	<beans profile="auke">
    		<context:property-placeholder location="<some_props_location>" />
    	</beans>
    	
    	
    </beans>
    ehache.xml:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
    	updateCheck="false">
    
    	<diskStore path="java.io.tmpdir/kerken_nl" />
    
    	<defaultCache eternal="false" maxElementsInMemory="1000"
    		overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0"
    		timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU" />
    	<cache name="byId" maxElementsInMemory="10000"
    			eternal="false"
    			overflowToDisk="false"
    			timeToIdleSeconds="300"
    			timeToLiveSeconds="600" />
    </ehcache>
    an exerpt from the DAO with @Cacheable annotated method:
    Code:
    @Repository
    public class BaseDao<T extends DatabaseEntity> {
    
    	private Logger logger = LoggerFactory.getLogger(getClass());
    	
    	private SessionFactory sessionFactory;
    
    	public BaseDao() {
    	}
    
    	@SuppressWarnings("unchecked")
    	@Transactional(readOnly = true)
    	@Cacheable(value="byId")
    	public T getById(Class<T> clazz, int id) {
    		return (T) getCurrentSession().get(clazz, id);
    	}
    The context and database.xml are used as context-param for the ContextLoaderListener.

    When setting the logging to DEBUG for net.sf.ehcache and org.springframework.cache, I can see that the caches are initialized, but I never see the log statements saying that objects are stored in the cache or retrieved from the cache.

    I've made a test-app that has 1 bean, with a method annotated with @Cacheable:
    Code:
    package nl.noppe.cache;
    
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    
    @Service
    public class CachedBean {
    
    	@Cacheable("cache")
    	public String getCachedValue(Class<?> clazz) {
    		System.out.println("no cache " + clazz.getName());
    		return "hello " + clazz.getName();
    	}
    }
    The main class:
    Code:
    package nl.noppe.cache;
    
    
    import java.util.List;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class CacheTest {
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/context.xml");
    		System.out.println(context.getBeanNamesForType(CachedBean.class).length);
    		CachedBean cachedBean = context.getBean(CachedBean.class);
    		
    		System.out.println(cachedBean.getCachedValue(String.class));
    		System.out.println(cachedBean.getCachedValue(List.class));
    		System.out.println(cachedBean.getCachedValue(String.class));
    		System.out.println(cachedBean.getCachedValue(List.class));
    			
    	}
    
    }
    context.xml:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans ...>
    
    	<context:component-scan base-package="nl.noppe.cache" />
    	
    	<cache:annotation-driven/>
    	
    	<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
    		<property name="cacheManager" ref="ehcache" />
    	</bean>
    	
    	<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
    		<property name="configLocation" value="classpath:ehcache.xml"/>
    	</bean>
    
    </beans>
    output:
    Code:
    2013-01-21 08:11:09,377 DEBUG [org.springframework.cache.annotation.AnnotationCacheOperationSource:108] Adding cacheable method 'getCachedValue' with attribute: [CacheableOperation[public java.lang.String nl.noppe.cache.CachedBean.getCachedValue(java.lang.Class)] caches=[cache] | condition='' | key='']
    2013-01-21 08:11:09,440 INFO  [org.springframework.cache.ehcache.EhCacheManagerFactoryBean:100] Initializing EHCache CacheManager
    2013-01-21 08:11:09,456 DEBUG [net.sf.ehcache.config.ConfigurationFactory:150] Configuring ehcache from InputStream
    2013-01-21 08:11:09,469 DEBUG [net.sf.ehcache.config.BeanHandler:271] Ignoring ehcache attribute xmlns:xsi
    2013-01-21 08:11:09,469 DEBUG [net.sf.ehcache.config.BeanHandler:271] Ignoring ehcache attribute xsi:noNamespaceSchemaLocation
    2013-01-21 08:11:09,471 DEBUG [net.sf.ehcache.config.DiskStoreConfiguration:141] Disk Store Path: /var/folders/bk/pq3_0vsn4d50k8dk868ld7mh0000gn/T/cachetest
    2013-01-21 08:11:09,485 DEBUG [net.sf.ehcache.util.PropertyUtil:88] propertiesString is null.
    2013-01-21 08:11:09,495 DEBUG [net.sf.ehcache.config.ConfigurationHelper:185] No CacheManagerEventListenerFactory class specified. Skipping...
    2013-01-21 08:11:09,545 DEBUG [net.sf.ehcache.Cache:954] No BootstrapCacheLoaderFactory class specified. Skipping...
    2013-01-21 08:11:09,546 DEBUG [net.sf.ehcache.Cache:928] CacheWriter factory not configured. Skipping...
    2013-01-21 08:11:09,547 DEBUG [net.sf.ehcache.config.ConfigurationHelper:96] No CacheExceptionHandlerFactory class specified. Skipping...
    2013-01-21 08:11:09,552 DEBUG [net.sf.ehcache.Cache:954] No BootstrapCacheLoaderFactory class specified. Skipping...
    2013-01-21 08:11:09,552 DEBUG [net.sf.ehcache.Cache:928] CacheWriter factory not configured. Skipping...
    2013-01-21 08:11:09,554 DEBUG [net.sf.ehcache.config.ConfigurationHelper:96] No CacheExceptionHandlerFactory class specified. Skipping...
    2013-01-21 08:11:09,571 DEBUG [net.sf.ehcache.store.MemoryStore:149] Initialized net.sf.ehcache.store.NotifyingMemoryStore for cache
    2013-01-21 08:11:09,578 DEBUG [net.sf.ehcache.Cache:1164] Initialised cache: cache
    2013-01-21 08:11:09,578 DEBUG [net.sf.ehcache.config.ConfigurationHelper:325] CacheDecoratorFactory not configured. Skipping for 'cache'.
    2013-01-21 08:11:09,579 DEBUG [net.sf.ehcache.config.ConfigurationHelper:354] CacheDecoratorFactory not configured for defaultCache. Skipping for 'cache'.
    1
    no cache java.lang.String
    hello java.lang.String
    no cache java.util.List
    hello java.util.List
    hello java.lang.String
    hello java.util.List
    you can see that it works, because the sysout 'no cache' + classname is not called for the 3-5 method call.

    what am i doing wrong?

  • #2
    And why should it work?!

    Your configuration duplicates your bean instances which lead to proxied (cache enabled) and unproxied (no caching). Assuming your *-servlet.xml is loaded by a DispatcherSerlvet and your other xml files by the ContextLoaderListener you are basically rendering your caching useless..

    Your api-servlet.xml is flawed as that is scanning for the same classes as the other component-scan, this duplicates the beans with unproxied instances and those are used by the controllers. This is because first the own context is scanned and when not found the parent (ContextLoaderListener).

    Disable the default filters in your api-servlet.xml and only scan for web related beans (@Controller).. Judging by your current configuration you probably think that it scans only for what you include but it scans for everything including what you include (including something doesn't disable the default scannning for @Component).

    Comment


    • #3
      Thanks for your reply Marten.

      Originally posted by Marten Deinum View Post
      Disable the default filters in your api-servlet.xml and only scan for web related beans (@Controller).. Judging by your current configuration you probably think that it scans only for what you include but it scans for everything including what you include (including something doesn't disable the default scannning for @Component).
      If I'm reading your reply correct, you are saying that I should modify the <context:component-scan> configuration in the api-servlet.xml and exclude the 'non-controller' packages?
      I'ts quite a stupid misconfiguration

      I'll give that a try.


      Regards,
      Auke

      Comment


      • #4
        The common thing is that you see something like this in the configuration

        ContextLoaderLIstener part...
        Code:
        <context:component-scan base-package="nl.bla.api" >
          <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        </context:component-scan>
        DispatcherServlet part...
        Code:
        <context:component-scan base-package="nl.bla.api" use-default-filters="false">
          <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        </context:component-scan>

        Comment


        • #5
          Hi Marten,

          Sorry for the late reply.
          I've tried your solution, but didn't work.

          I have managed to solve this by changing the configuration to:

          api-servlet.xml:
          Code:
           
          <context:component-scan base-package="nl.bla.api" use-default-filters="false">
                  <context:include-filter type="aspectj" expression="nl.bla.api..*Controller"/>
          </context:component-scan>
          context.xml:
          Code:
          <context:component-scan base-package="nl.bla.api" >
                  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
          </context:component-scan>

          Comment

          Working...
          X