4

I have tried to implement caching on Spring boot app several ways and this seems to be the right approach but it just logs that

CacheStatistics,CacheManager=urn.X-ehcache.jsr107-default-config,Cache=studentCache
Registering Ehcache MBean javax.cache:type=CacheStatistics,CacheManager=urn.X-ehcache.jsr107-default-config,Cache=studentCache

I have a event logger but I do not see any output from it:

@Component
public class EventLogger implements CacheEventListener<Object, Object> {

    private static final Logger LOGGER = LoggerFactory.getLogger(EventLogger.class);

    @Override
    public void onEvent(CacheEvent<?, ?> event) {
        LOGGER.info("Event: " + event.getType() + " Key: " + event.getKey() + " old value: " + event.getOldValue() + " new value: " + event.getNewValue());
    }
}

CacheConfig

@Configuration
public class CacheConfig {

    @Bean
    public JCacheManagerCustomizer cacheManagerCustomizer() {
        return new JCacheManagerCustomizer() {

            @Override
            public void customize(CacheManager cacheManager) {
                cacheManager.createCache("studentCache", new MutableConfiguration<>()
                        .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.MINUTES, 5)))
                        .setStoreByValue(false)
                        .setStatisticsEnabled(true));

            }
        };
    }

} 

Cache on method

@RequestMapping(method = GET)
@ResponseBody
Cacheable(value = "studetNode")
    public List<StudentNodeDto> findAll(HttpServletResponse response) {
         val studentNodes = service.findAll();

ehcache.xml located under resources

<config
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
        xmlns='http://www.ehcache.org/v3'
        xmlns:jsr107='http://www.ehcache.org/v3/jsr107'>

    <service>
        <jsr107:defaults>
            <jsr107:cache name="studentCache" template="heap-cache"/>
        </jsr107:defaults>
    </service>

    <cache-template name="heap-cache">
        <listeners>
            <listener>
                <class>org.terracotta.ehcache.EventLogger</class>
                <event-firing-mode>ASYNCHRONOUS</event-firing-mode>
                <event-ordering-mode>UNORDERED</event-ordering-mode>
                <events-to-fire-on>CREATED</events-to-fire-on>
                <events-to-fire-on>UPDATED</events-to-fire-on>
                <events-to-fire-on>EXPIRED</events-to-fire-on>
                <events-to-fire-on>REMOVED</events-to-fire-on>
                <events-to-fire-on>EVICTED</events-to-fire-on>
            </listener>
        </listeners>
        <resources>
            <heap unit="entries">2000</heap>
            <offheap unit="MB">100</offheap>
        </resources>
    </cache-template>
</config>

Gradle dependencies springBootVersion = '2.0.2.RELEASE'

    compile group: 'org.springframework.boot', name: 'spring-boot-starter', version: springBootVersion
        compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion
//Cache
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-cache', version: '2.1.1.RELEASE'
    compile group: 'org.ehcache', name: 'ehcache', version: '3.4.0'
    compile group: 'javax.cache', name: 'cache-api', version: '1.1.0'

I have looked over many post and blogs and it appears I am doing this correctl but I have to be wrong somewhere.

@Cacheable key on multiple method arguments

https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache

https://www.baeldung.com/spring-cache-tutorial

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html

https://www.baeldung.com/hibernate-second-level-cache

https://medium.com/@igorkosandyak/spring-boot-caching-d74591abe117

Advice?

---------------Update 1-----------------

I am getting an error saying:

    Error creating bean with name 'cacheManager' defined in class path 
    resource
 [org/springframework/boot/autoconfigure/cache/JCacheCacheConfiguration.class]: Unsatisfied dependency expressed through method 'cacheManager' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jCacheCacheManager' defined in class path resource [org/springframework/boot/autoconfigure/cache/JCacheCacheConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.cache.CacheManager]: Factory method 'jCacheCacheManager' threw exception; nested exception is org.ehcache.jsr107.MultiCacheException: [Exception 0] org.terracotta.ehcache.EventLogger

I get this when I added:

# caching
spring.cache.jcache.provider=org.ehcache.jsr107.EhcacheCachingProvider
spring.cache.jcache.config=classpath:ehcache.xml

Trace

restartedMain] heConfiguration$JCacheAvailableCondition : Condition JCacheCacheConfiguration.JCacheAvailableCondition on org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration matched due to AnyNestedCondition 1 matched 1 did not; NestedCondition on JCacheCacheConfiguration.JCacheAvailableCondition.CustomJCacheCacheManager @ConditionalOnSingleCandidate (types: javax.cache.CacheManager; SearchStrategy: all) did not find any beans; NestedCondition on JCacheCacheConfiguration.JCacheAvailableCondition.JCacheProvider JCache JCache provider specified

-------------- update 2------------------

I added the following to my gradle file

task showJarLocations {
    doLast {
        configurations.compile.resolve().each { file ->
            println file.canonicalPath
        }
    }
}

and the only jar showing with ehcache is:

/org.ehcache/ehcache/3.4.0/cac1f0840af0040a81401dfa55fa31a4ccc17932/ehcache-3.4.0.jar
and

javax.cache/cache-api/1.1.0/77bdcff7814076dfa61611b0db88487c515150b6/cache-api-1.1.0.jar

I have

spring.cache.jcache.provider=org.ehcache.jsr107.EhcacheCachingProvider
spring.cache.jcache.config=classpath:ehcache.xml

in application.properties too. This should explain why it fails when I add spring.cache.jcache.config=classpath:ehcache.xml.

Butin intellij it is in my project structure:

enter image description here

+--- org.springframework.boot:spring-boot-starter-cache:2.1.1.RELEASE
|    +--- org.springframework.boot:spring-boot-starter:2.1.1.RELEASE -> 2.0.2.RELEASE (*)
|    \--- org.springframework:spring-context-support:5.1.3.RELEASE -> 5.0.6.RELEASE
|         +--- org.springframework:spring-beans:5.0.6.RELEASE (*)
|         +--- org.springframework:spring-context:5.0.6.RELEASE (*)
|         \--- org.springframework:spring-core:5.0.6.RELEASE (*)
+--- org.ehcache:ehcache:3.4.0
|    \--- org.slf4j:slf4j-api:1.7.7 -> 1.7.25
+--- javax.cache:cache-api:1.1.0
+--- org.apache.tika:tika-core:1.19.1
+--- org.mapstruct:mapstruct-jdk8:1.2.0.Final
+--- org.projectlombok:lombok:1.18.2
Mike3355
  • 11,305
  • 24
  • 96
  • 184
  • your configuration working fine – GolamMazid Sajib Dec 09 '18 at 11:49
  • what difference do you see on the logs if you add properties spring.cache.jcache.provider=org.ehcache.jsr107.EhcacheCachingProvider spring.cache.jcache.config=classpath:ehcache.xml – pcoates Dec 11 '18 at 20:17
  • Do you see anymore on the logs if you set `logging.level.org.springframework.boot.autoconfigure.cache=trace` and `logging.level.org.ehcache=trace` – pcoates Dec 11 '18 at 20:41
  • @pcoates no output with the logs and if I add `=classpath:ehcache.xml` I get `Error creating bean with name 'cacheManager' defined in class path resource [org/springframework/boot/autoconfigure/cache/JCacheCacheConfiguration.class]: `. There is no change with `org.ehcache.jsr107.EhcacheCachingProvider ` – Mike3355 Dec 12 '18 at 08:36
  • That's odd, you should see something. I assume you've got `@EnableCaching` on you main app. Probably worth posting the annotations you've got on the main app, and the full set of dependencies you have in case there's something getting in the way. I notice in your post `Cacheable(value = "studetNode") I assume this is a typo as it should be studentNode, also needs an `@` on the annotation. – pcoates Dec 12 '18 at 08:54
  • @pcoates yes that was a typo, I will fix it and yes I have `@EnableCaching` on the main method with `@SpringBootApplication`. – Mike3355 Dec 12 '18 at 08:56
  • Can you post the stack trace for the error you get when you add the =classpath:ehcache.xml – pcoates Dec 12 '18 at 08:58
  • @pcoates `Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.cache.CacheManager]: Factory method 'jCacheCacheManager' threw exception; nested exception is org.ehcache.jsr107.MultiCacheException: [Exception 0] org.terracotta.ehcache.EventLogger` – Mike3355 Dec 12 '18 at 09:51

3 Answers3

3

I found that using the spring-boot-starter-cache can create some subtle problems. If your ehcache.xml isn't found or you misname the cache name, Spring appears to fallback to a generic cache implementation thus hiding the problem. Try removing spring-boot-starter-cache as a dependency and add:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
</dependency>

also add ehcache as an explicit dependency then see if that helps. You shouldn't need to even access a CacheManager since you are using ehcache.xml; the whole point of the xml is to make your configuration declarative and outside the code.

  • Thank you. I will give it a shot! Thanks for posting – Mike3355 Dec 16 '18 at 19:27
  • I tried and got this error ` Error creating bean with name 'objectMapperConfigurer' defined in class path resource [springfox/documentation/spring/web/SpringfoxWebMvcConfiguration.class]:` it was worth a shot. Thank you for posting. – Mike3355 Dec 17 '18 at 08:32
  • Did you remove your CacheConfig and try a stripped down ehcache.xml such as: – codemasterg Dec 18 '18 at 00:40
0
  • Ensure springboot app is configured with @EnableCaching
  • Ensure the name in @Cacheable matches your cache name (you have a mismatch in what you posted.)

    @Cacheable(value = "studentCache")
    
  • Remove your CacheConfig

  • Place your ehcache.xml file in a subfolder of resources to make sure you're not picking up one from some other jar

    e.g. `resources/myconfig/ehcache.xml`
    
  • Set the property in application.properties to tell spring where to find the config file

    spring.cache.jcache.config=classpath:myconfig/ehcache.xml
    
  • Use a simplified ehcache.xml. e.g.

    <config
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
        xmlns='http://www.ehcache.org/v3'
        xmlns:jsr107='http://www.ehcache.org/v3/jsr107'
        xsi:schemaLocation="
            http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
            http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
    
        <cache alias="studentCache" uses-template="heap-cache" />
    
        <cache-template name="heap-cache">
            <resources>
                <heap unit="entries">2000</heap>
                <offheap unit="MB">100</offheap>
            </resources>
        </cache-template>
    
    </config>
    

If it still fails, post the results showing how you know it fails.

The reason you need the CacheConfig when using the <jsr107:cache elements is that ehcache will just associate the template with the cache name. It doesn't create the cache. The template will be used when you create the cache programatically (i.e. in your CacheConfig). So if you try an ehcache.xml which defines the cache you don't need the CacheConfig

pcoates
  • 2,102
  • 1
  • 9
  • 20
0

Make your entity or dto implement Serializable. Worked in my case.