I'm experiencing a problem similar to the described in this question.
I have a test suite that runs fine in development environment. One of the tests fails when executed in Bitbucket Pipelines with the following exception:
org.springframework.dao.InvalidDataAccessApiUsageException: Cache[model.Role] is closed; nested exception is java.lang.IllegalStateException: Cache[model.Role] is closed
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:364)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
....
I want to try the accepted solution but I don't know how to apply it to my project. The second solution depends on ehcache.xml file. I don't have this file, everything is configured in JavaConfig. How can I adopt the proposed solutions for EhCache + JCache (JSR-107) in JavaConfig?
My cache configuration:
@Configuration
@EnableCaching
public class CacheConfig {
private final javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration =
Eh107Configuration.fromEhcacheCacheConfiguration(CacheConfigurationBuilder
.newCacheConfigurationBuilder(Object.class, Object.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(100, EntryUnit.ENTRIES))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(60)))
.build());
@Bean
public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
createIfNotExists(cm, "model.Role");
createIfNotExists(cm, "model.User.roles");
// ...
};
}
private void createIfNotExists(CacheManager cacheManager, String cacheName) {
if (cacheManager.getCache(cacheName) == null) {
cacheManager.createCache(cacheName, jcacheConfiguration);
}
}
}
Gradle dependencies:
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-cache'
implementation group: 'javax.cache', name: 'cache-api'
implementation group: 'org.ehcache', name: 'ehcache'
implementation group: 'org.hibernate', name: 'hibernate-jcache'
The failing test:
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class SecondLevelCacheTest {
@Autowired
private RoleRepository roleRepository;
private CacheManager manager;
@Before
public void initCacheManager() {
CachingProvider provider = Caching.getCachingProvider();
manager = provider.getCacheManager();
final String cacheRegion = "model.Role";
manager.getCache(cacheRegion).clear();
}
@Test
public final void givenEntityIsLoaded_thenItIsCached() {
final String cacheRegion = "model.Role";
boolean hasNext = manager.getCache(cacheRegion).iterator().hasNext();
final Role role = roleRepository.findByName("USER");
boolean hasNext2 = manager.getCache(cacheRegion).iterator().hasNext();
final Role role2 = roleRepository.findByName("USER");
Assert.assertFalse(hasNext);
Assert.assertTrue(hasNext2);
}
}
The most upvoted solution is "to set shared property to false in the testing context." How can I do this with regard to my configuration?
The proposed solution you are talking about is based on Ehcache 2. You are using Ehcache 3 (good for you), so it's not valid.
Spring will take care of closing the
CacheManager
so normally, you don't need to take care of anything. Also, you do not need to access it through theCachingProvider
. You can@Autowired
thejavax.cache.CacheManager
and that way you are sure to get the right one.However, you are using Hibernate. You should make sure that Spring and Hibernate are using the same
CacheManager
. The way to configure it depends on the Spring and Hibernate version.Can we have the full stack trace? Right now it feels like something is closing the
CacheManager
without deregister it from theCachingProvider
. This is impossible unless you are closing theorg.ehcache.CacheManager
without closing thejavax.cache.CacheManager
wrapping it. Closing the later will cause the deregistration.