diff --git a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheHelper.java b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheHelper.java index 1d87b7d9b578..a52955565be3 100644 --- a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheHelper.java +++ b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheHelper.java @@ -6,6 +6,7 @@ import javax.cache.CacheManager; import javax.cache.Caching; +import javax.cache.spi.CachingProvider; /** * @author Steve Ebersole @@ -19,6 +20,13 @@ public class JCacheHelper { public static CacheManager locateStandardCacheManager() { // unless configured differently, this is how JCacheRegionFactory // will locate the CacheManager to use - return Caching.getCachingProvider().getCacheManager(); + + CachingProvider cachingProvider = Caching.getCachingProvider(); + + // JRegionFactory::prepareForUse retrieves the class loader service from + // the service registry and registers it as the + // Since EHCache by itself doesn't use this class loader by itself, it needs to be injected here. + // It is set via JCacheRegionFactory::prepareForUse. S.a. https://github.com/ehcache/ehcache3/issues/3252 + return cachingProvider.getCacheManager( cachingProvider.getDefaultURI(), Caching.getDefaultClassLoader() ); } } diff --git a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/internal/JCacheRegionFactory.java b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/internal/JCacheRegionFactory.java index 8bae0fbe8dcc..dde8bc83a0e6 100644 --- a/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/internal/JCacheRegionFactory.java +++ b/hibernate-jcache/src/main/java/org/hibernate/cache/jcache/internal/JCacheRegionFactory.java @@ -23,6 +23,7 @@ import org.hibernate.cache.cfg.spi.DomainDataRegionConfig; import org.hibernate.cache.internal.DefaultCacheKeysFactory; import org.hibernate.cache.jcache.ConfigSettings; +import org.hibernate.cache.jcache.JCacheHelper; import org.hibernate.cache.jcache.MissingCacheStrategy; import org.hibernate.cache.spi.CacheKeysFactory; import org.hibernate.cache.spi.DomainDataRegion; @@ -43,6 +44,12 @@ public class JCacheRegionFactory extends RegionFactoryTemplate { private volatile CacheManager cacheManager; private volatile MissingCacheStrategy missingCacheStrategy; + // need to save the classloader from Caching.getDefaultClassLoader to reset it + // when this service is released again. + private volatile ClassLoader oldJsr107CacheClassLoader; + // in case caches were already set up beforehand with a different configuration + private volatile CacheManager preInitializedCacheManager; + @SuppressWarnings("unused") public JCacheRegionFactory() { this( DefaultCacheKeysFactory.INSTANCE ); @@ -85,7 +92,8 @@ protected DomainDataStorageAccess createDomainDataStorageAccess( protected Cache getOrCreateCache(String unqualifiedRegionName, SessionFactoryImplementor sessionFactory) { verifyStarted(); - assert !RegionNameQualifier.INSTANCE.isQualified( unqualifiedRegionName, sessionFactory.getSessionFactoryOptions() ); + assert !RegionNameQualifier.INSTANCE.isQualified( unqualifiedRegionName, + sessionFactory.getSessionFactoryOptions() ); final String qualifiedRegionName = RegionNameQualifier.INSTANCE.qualify( unqualifiedRegionName, @@ -94,6 +102,13 @@ protected Cache getOrCreateCache(String unqualifiedRegionName, S final Cache cache = cacheManager.getCache( qualifiedRegionName ); if ( cache == null ) { + if ( preInitializedCacheManager != null ) { + final Cache cacheFromBefore = preInitializedCacheManager.getCache( + qualifiedRegionName ); + if ( cacheFromBefore != null ) { + return cacheFromBefore; + } + } return createCache( qualifiedRegionName ); } return cache; @@ -110,7 +125,8 @@ protected Cache createCache(String regionName) { case CREATE: return cacheManager.createCache( regionName, new MutableConfiguration<>() ); case FAIL: - throw new CacheException( "On-the-fly creation of JCache Cache objects is not supported [" + regionName + "]" ); + throw new CacheException( + "On-the-fly creation of JCache Cache objects is not supported [" + regionName + "]" ); default: throw new IllegalStateException( "Unsupported missing cache strategy: " + missingCacheStrategy ); } @@ -121,7 +137,9 @@ protected boolean cacheExists(String unqualifiedRegionName, SessionFactoryImplem unqualifiedRegionName, sessionFactory.getSessionFactoryOptions() ); - return cacheManager.getCache( qualifiedRegionName ) != null; + return cacheManager.getCache( qualifiedRegionName ) != null || + (preInitializedCacheManager != null && + preInitializedCacheManager.getCache( qualifiedRegionName ) != null); } @Override @@ -155,9 +173,9 @@ protected StorageAccess createTimestampsRegionStorageAccess( } protected final String defaultRegionName(String regionName, SessionFactoryImplementor sessionFactory, - String defaultRegionName, List legacyDefaultRegionNames) { + String defaultRegionName, List legacyDefaultRegionNames) { if ( defaultRegionName.equals( regionName ) - && !cacheExists( regionName, sessionFactory ) ) { + && !cacheExists( regionName, sessionFactory ) ) { // Maybe the user configured caches explicitly with legacy names; try them and use the first that exists for ( String legacyDefaultRegionName : legacyDefaultRegionNames ) { @@ -172,7 +190,6 @@ protected final String defaultRegionName(String regionName, SessionFactoryImplem } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Lifecycle @@ -182,7 +199,14 @@ protected boolean isStarted() { } @Override - protected void prepareForUse(SessionFactoryOptions settings, Map configValues) { + protected void prepareForUse(SessionFactoryOptions settings, Map configValues) { + final ClassLoader serviceClassLoader = getServiceClassLoader( settings ); + if ( serviceClassLoader != null ) { + preInitializedCacheManager = JCacheHelper.locateStandardCacheManager(); + oldJsr107CacheClassLoader = Caching.getDefaultClassLoader(); + Caching.setDefaultClassLoader( serviceClassLoader ); + } + this.cacheManager = resolveCacheManager( settings, configValues ); if ( this.cacheManager == null ) { throw new CacheException( "Could not locate/create CacheManager" ); @@ -192,30 +216,38 @@ protected void prepareForUse(SessionFactoryOptions settings, Map ); } - protected CacheManager resolveCacheManager(SessionFactoryOptions settings, Map properties) { + protected CacheManager resolveCacheManager(SessionFactoryOptions settings, Map properties) { final Object explicitCacheManager = properties.get( ConfigSettings.CACHE_MANAGER ); if ( explicitCacheManager != null ) { return useExplicitCacheManager( settings, explicitCacheManager ); } - final CachingProvider cachingProvider = getCachingProvider( properties ); + final ClassLoader serviceClassLoader = getServiceClassLoader( settings ); + final CachingProvider cachingProvider = getCachingProvider( properties, serviceClassLoader ); final CacheManager cacheManager; final URI cacheManagerUri = getUri( settings, properties ); + final ClassLoader classLoader = getClassLoader( cachingProvider, serviceClassLoader ); if ( cacheManagerUri != null ) { - cacheManager = cachingProvider.getCacheManager( cacheManagerUri, getClassLoader( cachingProvider ) ); + cacheManager = cachingProvider.getCacheManager( cacheManagerUri, classLoader ); } else { - cacheManager = cachingProvider.getCacheManager( cachingProvider.getDefaultURI(), getClassLoader( cachingProvider ) ); + cacheManager = cachingProvider.getCacheManager( cachingProvider.getDefaultURI(), classLoader ); } return cacheManager; } - protected ClassLoader getClassLoader(CachingProvider cachingProvider) { - // todo (5.3) : shouldn't this use Hibernate's AggregatedClassLoader? - return cachingProvider.getDefaultClassLoader(); + private ClassLoader getServiceClassLoader(SessionFactoryOptions settings) { + final ClassLoaderService classLoaderService = settings.getServiceRegistry() + .getService( ClassLoaderService.class ); + return (classLoaderService == null) ? null : + classLoaderService.workWithClassLoader( classLoader -> classLoader ); + } + + protected ClassLoader getClassLoader(CachingProvider cachingProvider, ClassLoader serviceClassLoader) { + return (serviceClassLoader != null) ? serviceClassLoader : cachingProvider.getDefaultClassLoader(); } - protected URI getUri(SessionFactoryOptions settings, Map properties) { + protected URI getUri(SessionFactoryOptions settings, Map properties) { String cacheManagerUri = getProp( properties, ConfigSettings.CONFIG_URI ); if ( cacheManagerUri == null ) { return null; @@ -237,18 +269,18 @@ protected URI getUri(SessionFactoryOptions settings, Map properti } } - private String getProp(Map properties, String prop) { + private String getProp(Map properties, String prop) { return properties != null ? (String) properties.get( prop ) : null; } - protected CachingProvider getCachingProvider(final Map properties){ + protected CachingProvider getCachingProvider(final Map properties, ClassLoader classLoader) { final CachingProvider cachingProvider; final String provider = getProp( properties, ConfigSettings.PROVIDER ); if ( provider != null ) { - cachingProvider = Caching.getCachingProvider( provider ); + cachingProvider = Caching.getCachingProvider( provider, classLoader ); } else { - cachingProvider = Caching.getCachingProvider(); + cachingProvider = Caching.getCachingProvider( classLoader ); } return cachingProvider; } @@ -283,6 +315,9 @@ protected void releaseFromUse() { // - when the explicit `setting` passed to `#useExplicitCacheManager` is // a CacheManager instance cacheManager.close(); + if ( oldJsr107CacheClassLoader != null ) { + Caching.setDefaultClassLoader( oldJsr107CacheClassLoader ); + } } finally { cacheManager = null; diff --git a/hibernate-jcache/src/test/java/org/hibernate/orm/test/jcache/MissingCacheStrategyTest.java b/hibernate-jcache/src/test/java/org/hibernate/orm/test/jcache/MissingCacheStrategyTest.java index 02062834af1f..62f5aab3b1bf 100644 --- a/hibernate-jcache/src/test/java/org/hibernate/orm/test/jcache/MissingCacheStrategyTest.java +++ b/hibernate-jcache/src/test/java/org/hibernate/orm/test/jcache/MissingCacheStrategyTest.java @@ -11,15 +11,16 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cache.CacheException; import org.hibernate.cache.jcache.ConfigSettings; +import org.hibernate.cache.jcache.JCacheHelper; import org.hibernate.cache.spi.SecondLevelCacheLogger; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.service.spi.ServiceException; - import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.testing.logger.LoggerInspectionRule; import org.hibernate.testing.logger.Triggerable; import org.junit.Rule; import org.junit.Test; +import javax.cache.CacheManager; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; @@ -63,11 +64,14 @@ public void testMissingCacheStrategyFail() { } catch (ServiceException expected) { assertTyping( CacheException.class, expected.getCause() ); - assertThat( expected.getMessage(), startsWith( "Unable to create requested service [" + org.hibernate.cache.spi.CacheImplementor.class.getName() + "]" ) ); - assertThat( expected.getCause().getMessage(), startsWith( "On-the-fly creation of JCache Cache objects is not supported" ) ); + assertThat( expected.getMessage(), startsWith( + "Unable to create requested service [" + org.hibernate.cache.spi.CacheImplementor.class.getName() + "]" ) ); + assertThat( expected.getCause().getMessage(), + startsWith( "On-the-fly creation of JCache Cache objects is not supported" ) ); } catch (CacheException expected) { - assertThat( expected.getMessage(), equalTo( "On-the-fly creation of JCache Cache objects is not supported" ) ); + assertThat( expected.getMessage(), + equalTo( "On-the-fly creation of JCache Cache objects is not supported" ) ); } } @@ -129,15 +133,26 @@ private void doTestMissingCacheStrategyCreateWarn(Consumer triggerables = new HashMap<>(); // first, lets make sure that the regions used for model caches exist @@ -169,7 +184,7 @@ private void doTestMissingCacheStrategyFailLegacyNames(String[] existingLegacyCa } // and now let's try to build the standard testing SessionFactory - try ( SessionFactoryImplementor ignored = TestHelper.buildStandardSessionFactory( + try (SessionFactoryImplementor ignored = TestHelper.buildStandardSessionFactory( builder -> builder.applySetting( ConfigSettings.MISSING_CACHE_STRATEGY, "fail" ) ) ) { // The session should start successfully (if we reach this line, we're good)