Skip to content

Commit 6d9feba

Browse files
committed
HHH-13046 ClassLoaderService in JCacheRegionFactory
This enables the use of caching in scenarios where a non-standard class loader is registered using the service registry. Unfortunately, since the service registry lifecycle is tied to the lifecycle of the SessionFactory, the required initialization happens too late for some of the test cases to "just work". To keep compatibility with the specs from the test suite, some hackery is required: the JCacheRegionFactory has to hold on to some defaults from before being set up.
1 parent 985740e commit 6d9feba

File tree

3 files changed

+85
-27
lines changed

3 files changed

+85
-27
lines changed

hibernate-jcache/src/main/java/org/hibernate/cache/jcache/JCacheHelper.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import javax.cache.CacheManager;
88
import javax.cache.Caching;
9+
import javax.cache.spi.CachingProvider;
910

1011
/**
1112
* @author Steve Ebersole
@@ -19,6 +20,13 @@ public class JCacheHelper {
1920
public static CacheManager locateStandardCacheManager() {
2021
// unless configured differently, this is how JCacheRegionFactory
2122
// will locate the CacheManager to use
22-
return Caching.getCachingProvider().getCacheManager();
23+
24+
CachingProvider cachingProvider = Caching.getCachingProvider();
25+
26+
// JRegionFactory::prepareForUse retrieves the class loader service from
27+
// the service registry and registers it as the
28+
// Since EHCache by itself doesn't use this class loader by itself, it needs to be injected here.
29+
// It is set via JCacheRegionFactory::prepareForUse. S.a. https://github.com/ehcache/ehcache3/issues/3252
30+
return cachingProvider.getCacheManager( cachingProvider.getDefaultURI(), Caching.getDefaultClassLoader() );
2331
}
2432
}

hibernate-jcache/src/main/java/org/hibernate/cache/jcache/internal/JCacheRegionFactory.java

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.hibernate.cache.cfg.spi.DomainDataRegionConfig;
2424
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
2525
import org.hibernate.cache.jcache.ConfigSettings;
26+
import org.hibernate.cache.jcache.JCacheHelper;
2627
import org.hibernate.cache.jcache.MissingCacheStrategy;
2728
import org.hibernate.cache.spi.CacheKeysFactory;
2829
import org.hibernate.cache.spi.DomainDataRegion;
@@ -43,6 +44,12 @@ public class JCacheRegionFactory extends RegionFactoryTemplate {
4344
private volatile CacheManager cacheManager;
4445
private volatile MissingCacheStrategy missingCacheStrategy;
4546

47+
// need to save the classloader from Caching.getDefaultClassLoader to reset it
48+
// when this service is released again.
49+
private volatile ClassLoader oldJsr107CacheClassLoader;
50+
// in case caches were already set up beforehand with a different configuration
51+
private volatile CacheManager preInitializedCacheManager;
52+
4653
@SuppressWarnings("unused")
4754
public JCacheRegionFactory() {
4855
this( DefaultCacheKeysFactory.INSTANCE );
@@ -85,7 +92,8 @@ protected DomainDataStorageAccess createDomainDataStorageAccess(
8592

8693
protected Cache<Object, Object> getOrCreateCache(String unqualifiedRegionName, SessionFactoryImplementor sessionFactory) {
8794
verifyStarted();
88-
assert !RegionNameQualifier.INSTANCE.isQualified( unqualifiedRegionName, sessionFactory.getSessionFactoryOptions() );
95+
assert !RegionNameQualifier.INSTANCE.isQualified( unqualifiedRegionName,
96+
sessionFactory.getSessionFactoryOptions() );
8997

9098
final String qualifiedRegionName = RegionNameQualifier.INSTANCE.qualify(
9199
unqualifiedRegionName,
@@ -94,6 +102,13 @@ protected Cache<Object, Object> getOrCreateCache(String unqualifiedRegionName, S
94102

95103
final Cache<Object, Object> cache = cacheManager.getCache( qualifiedRegionName );
96104
if ( cache == null ) {
105+
if ( preInitializedCacheManager != null ) {
106+
final Cache<Object, Object> cacheFromBefore = preInitializedCacheManager.getCache(
107+
qualifiedRegionName );
108+
if ( cacheFromBefore != null ) {
109+
return cacheFromBefore;
110+
}
111+
}
97112
return createCache( qualifiedRegionName );
98113
}
99114
return cache;
@@ -110,7 +125,8 @@ protected Cache<Object, Object> createCache(String regionName) {
110125
case CREATE:
111126
return cacheManager.createCache( regionName, new MutableConfiguration<>() );
112127
case FAIL:
113-
throw new CacheException( "On-the-fly creation of JCache Cache objects is not supported [" + regionName + "]" );
128+
throw new CacheException(
129+
"On-the-fly creation of JCache Cache objects is not supported [" + regionName + "]" );
114130
default:
115131
throw new IllegalStateException( "Unsupported missing cache strategy: " + missingCacheStrategy );
116132
}
@@ -121,7 +137,9 @@ protected boolean cacheExists(String unqualifiedRegionName, SessionFactoryImplem
121137
unqualifiedRegionName,
122138
sessionFactory.getSessionFactoryOptions()
123139
);
124-
return cacheManager.getCache( qualifiedRegionName ) != null;
140+
return cacheManager.getCache( qualifiedRegionName ) != null ||
141+
(preInitializedCacheManager != null &&
142+
preInitializedCacheManager.getCache( qualifiedRegionName ) != null);
125143
}
126144

127145
@Override
@@ -155,9 +173,9 @@ protected StorageAccess createTimestampsRegionStorageAccess(
155173
}
156174

157175
protected final String defaultRegionName(String regionName, SessionFactoryImplementor sessionFactory,
158-
String defaultRegionName, List<String> legacyDefaultRegionNames) {
176+
String defaultRegionName, List<String> legacyDefaultRegionNames) {
159177
if ( defaultRegionName.equals( regionName )
160-
&& !cacheExists( regionName, sessionFactory ) ) {
178+
&& !cacheExists( regionName, sessionFactory ) ) {
161179
// Maybe the user configured caches explicitly with legacy names; try them and use the first that exists
162180

163181
for ( String legacyDefaultRegionName : legacyDefaultRegionNames ) {
@@ -172,7 +190,6 @@ protected final String defaultRegionName(String regionName, SessionFactoryImplem
172190
}
173191

174192

175-
176193
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
177194
// Lifecycle
178195

@@ -182,7 +199,14 @@ protected boolean isStarted() {
182199
}
183200

184201
@Override
185-
protected void prepareForUse(SessionFactoryOptions settings, Map<String,Object> configValues) {
202+
protected void prepareForUse(SessionFactoryOptions settings, Map<String, Object> configValues) {
203+
final ClassLoader serviceClassLoader = getServiceClassLoader( settings );
204+
if ( serviceClassLoader != null ) {
205+
preInitializedCacheManager = JCacheHelper.locateStandardCacheManager();
206+
oldJsr107CacheClassLoader = Caching.getDefaultClassLoader();
207+
Caching.setDefaultClassLoader( serviceClassLoader );
208+
}
209+
186210
this.cacheManager = resolveCacheManager( settings, configValues );
187211
if ( this.cacheManager == null ) {
188212
throw new CacheException( "Could not locate/create CacheManager" );
@@ -192,30 +216,38 @@ protected void prepareForUse(SessionFactoryOptions settings, Map<String,Object>
192216
);
193217
}
194218

195-
protected CacheManager resolveCacheManager(SessionFactoryOptions settings, Map<String,Object> properties) {
219+
protected CacheManager resolveCacheManager(SessionFactoryOptions settings, Map<String, Object> properties) {
196220
final Object explicitCacheManager = properties.get( ConfigSettings.CACHE_MANAGER );
197221
if ( explicitCacheManager != null ) {
198222
return useExplicitCacheManager( settings, explicitCacheManager );
199223
}
200224

201-
final CachingProvider cachingProvider = getCachingProvider( properties );
225+
final ClassLoader serviceClassLoader = getServiceClassLoader( settings );
226+
final CachingProvider cachingProvider = getCachingProvider( properties, serviceClassLoader );
202227
final CacheManager cacheManager;
203228
final URI cacheManagerUri = getUri( settings, properties );
229+
final ClassLoader classLoader = getClassLoader( cachingProvider, serviceClassLoader );
204230
if ( cacheManagerUri != null ) {
205-
cacheManager = cachingProvider.getCacheManager( cacheManagerUri, getClassLoader( cachingProvider ) );
231+
cacheManager = cachingProvider.getCacheManager( cacheManagerUri, classLoader );
206232
}
207233
else {
208-
cacheManager = cachingProvider.getCacheManager( cachingProvider.getDefaultURI(), getClassLoader( cachingProvider ) );
234+
cacheManager = cachingProvider.getCacheManager( cachingProvider.getDefaultURI(), classLoader );
209235
}
210236
return cacheManager;
211237
}
212238

213-
protected ClassLoader getClassLoader(CachingProvider cachingProvider) {
214-
// todo (5.3) : shouldn't this use Hibernate's AggregatedClassLoader?
215-
return cachingProvider.getDefaultClassLoader();
239+
private ClassLoader getServiceClassLoader(SessionFactoryOptions settings) {
240+
final ClassLoaderService classLoaderService = settings.getServiceRegistry()
241+
.getService( ClassLoaderService.class );
242+
return (classLoaderService == null) ? null :
243+
classLoaderService.workWithClassLoader( classLoader -> classLoader );
244+
}
245+
246+
protected ClassLoader getClassLoader(CachingProvider cachingProvider, ClassLoader serviceClassLoader) {
247+
return (serviceClassLoader != null) ? serviceClassLoader : cachingProvider.getDefaultClassLoader();
216248
}
217249

218-
protected URI getUri(SessionFactoryOptions settings, Map<String,Object> properties) {
250+
protected URI getUri(SessionFactoryOptions settings, Map<String, Object> properties) {
219251
String cacheManagerUri = getProp( properties, ConfigSettings.CONFIG_URI );
220252
if ( cacheManagerUri == null ) {
221253
return null;
@@ -237,18 +269,18 @@ protected URI getUri(SessionFactoryOptions settings, Map<String,Object> properti
237269
}
238270
}
239271

240-
private String getProp(Map<String,Object> properties, String prop) {
272+
private String getProp(Map<String, Object> properties, String prop) {
241273
return properties != null ? (String) properties.get( prop ) : null;
242274
}
243275

244-
protected CachingProvider getCachingProvider(final Map<String,Object> properties){
276+
protected CachingProvider getCachingProvider(final Map<String, Object> properties, ClassLoader classLoader) {
245277
final CachingProvider cachingProvider;
246278
final String provider = getProp( properties, ConfigSettings.PROVIDER );
247279
if ( provider != null ) {
248-
cachingProvider = Caching.getCachingProvider( provider );
280+
cachingProvider = Caching.getCachingProvider( provider, classLoader );
249281
}
250282
else {
251-
cachingProvider = Caching.getCachingProvider();
283+
cachingProvider = Caching.getCachingProvider( classLoader );
252284
}
253285
return cachingProvider;
254286
}
@@ -283,6 +315,9 @@ protected void releaseFromUse() {
283315
// - when the explicit `setting` passed to `#useExplicitCacheManager` is
284316
// a CacheManager instance
285317
cacheManager.close();
318+
if ( oldJsr107CacheClassLoader != null ) {
319+
Caching.setDefaultClassLoader( oldJsr107CacheClassLoader );
320+
}
286321
}
287322
finally {
288323
cacheManager = null;

hibernate-jcache/src/test/java/org/hibernate/orm/test/jcache/MissingCacheStrategyTest.java

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@
1111
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
1212
import org.hibernate.cache.CacheException;
1313
import org.hibernate.cache.jcache.ConfigSettings;
14+
import org.hibernate.cache.jcache.JCacheHelper;
1415
import org.hibernate.cache.spi.SecondLevelCacheLogger;
1516
import org.hibernate.engine.spi.SessionFactoryImplementor;
1617
import org.hibernate.service.spi.ServiceException;
17-
1818
import org.hibernate.testing.junit4.BaseUnitTestCase;
1919
import org.hibernate.testing.logger.LoggerInspectionRule;
2020
import org.hibernate.testing.logger.Triggerable;
2121
import org.junit.Rule;
2222
import org.junit.Test;
23+
import javax.cache.CacheManager;
2324

2425
import static org.hamcrest.CoreMatchers.equalTo;
2526
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -63,11 +64,14 @@ public void testMissingCacheStrategyFail() {
6364
}
6465
catch (ServiceException expected) {
6566
assertTyping( CacheException.class, expected.getCause() );
66-
assertThat( expected.getMessage(), startsWith( "Unable to create requested service [" + org.hibernate.cache.spi.CacheImplementor.class.getName() + "]" ) );
67-
assertThat( expected.getCause().getMessage(), startsWith( "On-the-fly creation of JCache Cache objects is not supported" ) );
67+
assertThat( expected.getMessage(), startsWith(
68+
"Unable to create requested service [" + org.hibernate.cache.spi.CacheImplementor.class.getName() + "]" ) );
69+
assertThat( expected.getCause().getMessage(),
70+
startsWith( "On-the-fly creation of JCache Cache objects is not supported" ) );
6871
}
6972
catch (CacheException expected) {
70-
assertThat( expected.getMessage(), equalTo( "On-the-fly creation of JCache Cache objects is not supported" ) );
73+
assertThat( expected.getMessage(),
74+
equalTo( "On-the-fly creation of JCache Cache objects is not supported" ) );
7175
}
7276
}
7377

@@ -129,15 +133,26 @@ private void doTestMissingCacheStrategyCreateWarn(Consumer<StandardServiceRegist
129133

130134
@Test
131135
public void testMissingCacheStrategyFailLegacyNames1() {
132-
doTestMissingCacheStrategyFailLegacyNames( TestHelper.queryRegionLegacyNames1, TestHelper.queryRegionLegacyNames2 );
136+
doTestMissingCacheStrategyFailLegacyNames( TestHelper.queryRegionLegacyNames1,
137+
TestHelper.queryRegionLegacyNames2 );
133138
}
134139

135140
@Test
136141
public void testMissingCacheStrategyFailLegacyNames2() {
137-
doTestMissingCacheStrategyFailLegacyNames( TestHelper.queryRegionLegacyNames2, TestHelper.queryRegionLegacyNames1 );
142+
doTestMissingCacheStrategyFailLegacyNames( TestHelper.queryRegionLegacyNames2,
143+
TestHelper.queryRegionLegacyNames1 );
138144
}
139145

140146
private void doTestMissingCacheStrategyFailLegacyNames(String[] existingLegacyCaches, String[] nonExistingLegacyCaches) {
147+
// clean up global state from previous tests
148+
final CacheManager standardCacheManager = JCacheHelper.locateStandardCacheManager();
149+
for ( String regionName : existingLegacyCaches ) {
150+
standardCacheManager.destroyCache( TestHelper.prefix( regionName ) );
151+
}
152+
for ( String regionName : nonExistingLegacyCaches ) {
153+
standardCacheManager.destroyCache( TestHelper.prefix( regionName ) );
154+
}
155+
141156
Map<String, Triggerable> triggerables = new HashMap<>();
142157

143158
// first, lets make sure that the regions used for model caches exist
@@ -169,7 +184,7 @@ private void doTestMissingCacheStrategyFailLegacyNames(String[] existingLegacyCa
169184
}
170185

171186
// and now let's try to build the standard testing SessionFactory
172-
try ( SessionFactoryImplementor ignored = TestHelper.buildStandardSessionFactory(
187+
try (SessionFactoryImplementor ignored = TestHelper.buildStandardSessionFactory(
173188
builder -> builder.applySetting( ConfigSettings.MISSING_CACHE_STRATEGY, "fail" )
174189
) ) {
175190
// The session should start successfully (if we reach this line, we're good)

0 commit comments

Comments
 (0)