1
votes

My application uses Spring 4.3.x, EhCache 3.6 and javax Cache 1.1.0. Here is how I've configured javax CacheManager in my application:

    <bean id="jCacheManager" class="org.springframework.cache.jcache.JCacheCacheManager">
        <property name="cacheManager" ref="appCacheManagerFactoryBean" />
    </bean>

    <bean id="appCacheManagerFactoryBean" class="com.example.AppCacheManagerFactoryBean"/>

The AppCacheManagerFactoryBean (which is just a customized version of JCacheManagerFactoryBean) helps me to configure a global persistence directory for my app. Here is how it looks:

public class AppCacheManagerFactoryBean implements FactoryBean<CacheManager>, InitializingBean,
    DisposableBean {

@Value("${cache.persistenceDir}")
private String persistenceDir;

private CacheManager cacheManager;

@Override
public void afterPropertiesSet() {
    this.cacheManager = buildCacheManager();
}

private CacheManager buildCacheManager()
{
    EhcacheCachingProvider cachingProvider = (EhcacheCachingProvider) Caching.getCachingProvider();
    DefaultConfiguration defaultConfiguration = new DefaultConfiguration(cachingProvider.getDefaultClassLoader(),
            new DefaultPersistenceConfiguration(new File(persistenceDir)));
    return cachingProvider.getCacheManager(cachingProvider.getDefaultURI(), defaultConfiguration);
}


@Override
public CacheManager getObject() {
    return this.cacheManager;
}

@Override
public Class<?> getObjectType() {
    return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class);
}

@Override
public boolean isSingleton() {
    return true;
}


@Override
public void destroy() {
    this.cacheManager.close();
}
}

Here's how I define caches. I use Ehcache API to create my caches as some of the features my caches need are not available through JCache API.

EhcacheManager ehcacheManager = jCacheCacheManager.getCacheManager().unwrap(EhcacheManager.class);
ehcacheManager.createCache("foo", CacheConfigurationBuilder.newCacheConfigurationBuilder(
                String.class, Foo.class,
                ResourcePoolsBuilder.newResourcePoolsBuilder()
                        .heap(1)
                        .offheap(1, MemoryUnit.GB)
                        .disk(5, MemoryUnit.GB)
        ));

When I try to retrieve a cache from the CacheManager elsewhere in my app, a null pointer exception is thrown.

Caching.getCachingProvider().getCacheManager().getCache("foo");

However, if I retrieve the cache after invoking the getCacheNames() method in CacheManager, the cache is fetched normally.

 Caching.getCachingProvider().getCacheManager().getCacheNames();
 Caching.getCachingProvider().getCacheManager().getCache("foo");

What have I missed? Please help me.

1

1 Answers

0
votes

My first question would be: "Why not using the built-in support of Spring-cache?" It won't need to do that. The JCacheCacheManager will take care of everything.

Then, your problem is that the cache is created directly in Ehcache without passing through the JSR107 layer. Calling getCacheNames() causes a refresh of the cache list in JSR107 to make it work. However, I'm not sure this global behavior is intended. But it is the way it works.

The solution is to create a cache as expected which is through the JSR107 layer. It looks like this

CacheManager cacheManager = jcacheCacheManager.getCacheManager();
cacheManager.createCache("foo",
    Eh107Configuration.fromEhcacheCacheConfiguration(
        CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Foo.class, ResourcePoolsBuilder.newResourcePoolsBuilder()
                    .heap(1)
                    .offheap(1, MemoryUnit.GB)
                    .disk(5, MemoryUnit.GB))
            .build()));