Reputation: 1324
I'm trying to create a generic spring boot backend template based on a JHipster generated one (because some apps are not going to be maintained by me and other spring devs may have to adapt to jhipster in order to make changes the jhipster way). That said, the process I followed to achieve this was: generate two jhipster projects with the following settings:
{
"generator-jhipster": {
"promptValues": {
"packageName": "com.mypackage.springtemplate",
"nativeLanguage": "en"
},
"jhipsterVersion": "5.7.2",
"applicationType": "monolith",
"baseName": "springtemplate",
"packageName": "com.mypackage.springtemplate",
"packageFolder": "com/mypackage/springtemplate",
"serverPort": "8080",
"authenticationType": "jwt",
"cacheProvider": "ehcache",
"enableHibernateCache": true,
"websocket": "spring-websocket",
"databaseType": "sql",
"devDatabaseType": "mysql",
"prodDatabaseType": "mysql",
"searchEngine": false,
"messageBroker": false,
"serviceDiscoveryType": false,
"buildTool": "maven",
"enableSwaggerCodegen": false,
"jwtSecretKey": "****",
"clientFramework": "angularX",
"useSass": true,
"clientPackageManager": "npm",
"testFrameworks": [],
"jhiPrefix": "jhi",
"otherModules": [],
"enableTranslation": true,
"nativeLanguage": "en",
"languages": ["en", "es"]
}
}
One of them was for having it as a reference. The second was the project to take jhipster stuff out. The steps I followed were:
These are the primary structure changes the project had:
The thing is when I try to run the project I get the following error with Hibernate 2nd level cache / ehcache:
Caused by: java.lang.IllegalStateException: All Hibernate caches should be created upfront. Please update CacheConfiguration.java to add com.mypackage.springtemplate.domain.User
at com.mypackage.springtemplate.config.cache.NoDefaultJCacheRegionFactory.createCache(NoDefaultJCacheRegionFactory.java:24)
at org.hibernate.cache.jcache.JCacheRegionFactory.getOrCreateCache(JCacheRegionFactory.java:190)
at org.hibernate.cache.jcache.JCacheRegionFactory.buildEntityRegion(JCacheRegionFactory.java:113)
at org.hibernate.cache.spi.RegionFactory.buildEntityRegion(RegionFactory.java:132)
The application-dev.yml hibernate config is pointing to the new local classes:
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
database: MYSQL
show-sql: true
properties:
hibernate.id.new_generator_mappings: true
hibernate.connection.provider_disables_autocommit: true
hibernate.cache.use_second_level_cache: true
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: true
hibernate.cache.region.factory_class: com.mypackage.springtemplate.config.cache.BeanClassLoaderAwareJCacheRegionFactory
Cache associated classes remain the same...
BeanClassLoaderAwareJCacheRegionFactory.java
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import java.util.Properties;
import javax.cache.CacheException;
import javax.cache.CacheManager;
import javax.cache.spi.CachingProvider;
/**
* Fixes Spring classloader issues that were introduced in Spring Boot 2.0.3.
*
* This allows to use the same classloader for ehcache, both for the Spring Cache abstraction and for the Hibernate
* 2nd level cache.
*
* See https://github.com/jhipster/generator-jhipster/issues/7783 for more information.
*/
public class BeanClassLoaderAwareJCacheRegionFactory extends NoDefaultJCacheRegionFactory {
private static volatile ClassLoader classLoader;
@Override
protected CacheManager getCacheManager(Properties properties) {
Objects.requireNonNull(classLoader, "Please set Spring's classloader in the setBeanClassLoader " +
"method before using this class in Hibernate");
CachingProvider cachingProvider = getCachingProvider( properties );
String cacheManagerUri = getProp( properties, CONFIG_URI );
URI uri = getUri(cachingProvider, cacheManagerUri);
CacheManager cacheManager = cachingProvider.getCacheManager(uri, classLoader);
// To prevent some class loader memory leak this might cause
setBeanClassLoader(null);
return cacheManager;
}
private URI getUri(CachingProvider cachingProvider, String cacheManagerUri) {
URI uri;
if (cacheManagerUri != null) {
try {
uri = new URI(cacheManagerUri);
}
catch (URISyntaxException e) {
throw new CacheException("Couldn't create URI from " + cacheManagerUri, e);
}
}
else {
uri = cachingProvider.getDefaultURI();
}
return uri;
}
/**
* This method must be called from a Spring Bean to get the classloader.
* For example: BeanClassLoaderAwareJCacheRegionFactory.setBeanClassLoader(this.getClass().getClassLoader());
*
* @param classLoader The Spring classloader
*/
public static void setBeanClassLoader(ClassLoader classLoader) {
BeanClassLoaderAwareJCacheRegionFactory.classLoader = classLoader;
}
}
NoDefaultJCacheRegionFactory.java
import java.util.Properties;
import javax.cache.Cache;
import org.hibernate.cache.jcache.JCacheRegionFactory;
import org.hibernate.cache.spi.CacheDataDescription;
/**
* Extends the default {@code JCacheRegionFactory} but makes sure all caches already exist to prevent
* spontaneous creation of badly configured caches (e.g. {@code new MutableConfiguration()}.
*
* See http://www.ehcache.org/blog/2017/03/15/spontaneous-cache-creation.html for more information.
*/
@SuppressWarnings("serial")
public class NoDefaultJCacheRegionFactory extends JCacheRegionFactory {
public static final String EXCEPTION_MESSAGE = "All Hibernate caches should be created upfront. " +
"Please update CacheConfiguration.java to add";
@Override
protected Cache<Object, Object> createCache(String regionName, Properties properties, CacheDataDescription
metadata) {
throw new IllegalStateException(EXCEPTION_MESSAGE + " " + regionName);
}
}
CacheConfiguration.java
import java.time.Duration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ExpiryPolicyBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.mypackage.springtemplate.config.properties.ApplicationProperties;
@Configuration
@EnableCaching
public class CacheConfiguration {
private final javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration;
public CacheConfiguration(ApplicationProperties applicationProperties) {
BeanClassLoaderAwareJCacheRegionFactory.setBeanClassLoader(this.getClass().getClassLoader());
ApplicationProperties.Cache.Ehcache ehcache =
applicationProperties.getCache().getEhcache();
jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration(
CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class,
ResourcePoolsBuilder.heap(ehcache.getMaxEntries()))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ehcache.getTimeToLiveSeconds())))
.build());
}
@Bean
public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
cm.createCache(com.mypackage.springtemplate.repository.UserRepository.USERS_BY_LOGIN_CACHE, jcacheConfiguration);
cm.createCache(com.mypackage.springtemplate.repository.UserRepository.USERS_BY_EMAIL_CACHE, jcacheConfiguration);
cm.createCache(com.mypackage.springtemplate.domain.User.class.getName(), jcacheConfiguration);
cm.createCache(com.mypackage.springtemplate.domain.Authority.class.getName(), jcacheConfiguration);
cm.createCache(com.mypackage.springtemplate.domain.User.class.getName() + ".authorities", jcacheConfiguration);
};
}
}
Already checked this question but dave's purpose is different from mine. In fact, I'm not doing any changes to jhipster's generated caching implementation.
Is there any thing I'm missing? Is liquibase maybe doing something I missed?
Upvotes: 1
Views: 2690
Reputation: 5731
It is also possible that CacheConfiguration.cacheManagerCustomizer
isn't called at all. This will mean you have broken the cache configuration and are probably using no cache or a simple cache, not Ehcache. Please check that javax.cache
is correctly in your classpath.
The other possibility is that you are not using the same CacheManage
for Hibernate and Spring. Please debug into EhcacheCachingProvider
and check the only one CacheManager
is created. If that's not the case, you need to look at what's different. It is usually the class loader but the BeanClassLoaderAwareJCacheRegionFactory
should normally prevent that.
Upvotes: 1