Cache is closed causing an exception while running test suite
Asked Answered
N

3

6

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?

Nacre answered 10/11, 2018 at 0:39 Comment(0)
G
3

Annotate your failing test class with @AutoConfigureCache. By default, this annotation will install a NoOpCacheManager, i.e. a basic, no operation CacheManager implementation suitable for disabling caching, typically used for backing cache declarations without an actual backing store. It will simply accept any items into the cache not actually storing them.

Spring Documentation on @AutoConfigureCache

Spring Documentation on @NoOpCacheManager

Gusto answered 15/1, 2021 at 7:54 Comment(0)
W
1

One option is to provide a custom CachingProvider that does not share CacheManager-s. An example solution can be found here.

Werewolf answered 3/2, 2020 at 20:4 Comment(0)
N
0

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 the CachingProvider. You can @Autowired the javax.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 the CachingProvider. This is impossible unless you are closing the org.ehcache.CacheManagerwithout closing the javax.cache.CacheManager wrapping it. Closing the later will cause the deregistration.

Nickel answered 25/11, 2018 at 3:7 Comment(1)
Hello. Sorry for posting in old question, I am facing the same error here for jhipster application (Spring boot 2.7 + ehcache 3.10.0) I tried all suggestions above but none of them did work. Any idea please ?Altamira

© 2022 - 2024 — McMap. All rights reserved.