Skip to content

Commit 8413c6d

Browse files
committed
HHH-19230 Avoid class loader leak in enhancement and improve bytebuddy type caching efficiency
1 parent 6ea041f commit 8413c6d

File tree

6 files changed

+123
-162
lines changed

6 files changed

+123
-162
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.bytecode.enhance.internal.bytebuddy;
6+
7+
import net.bytebuddy.dynamic.ClassFileLocator;
8+
import net.bytebuddy.pool.TypePool;
9+
10+
/**
11+
* A simple cache provider that allows overriding the resolution for the class that is currently being enhanced.
12+
*/
13+
final class EnhancerCacheProvider extends TypePool.CacheProvider.Simple {
14+
15+
private final ThreadLocal<EnhancementState> enhancementState = new ThreadLocal<>();
16+
17+
@Override
18+
public TypePool.Resolution find(final String name) {
19+
final EnhancementState enhancementState = getEnhancementState();
20+
if ( enhancementState != null && enhancementState.getClassName().equals( name ) ) {
21+
return enhancementState.getTypePoolResolution();
22+
}
23+
return super.find( name );
24+
}
25+
26+
EnhancementState getEnhancementState() {
27+
return enhancementState.get();
28+
}
29+
30+
void setEnhancementState(EnhancementState state) {
31+
enhancementState.set( state );
32+
}
33+
34+
void removeEnhancementState() {
35+
enhancementState.remove();
36+
}
37+
38+
static final class EnhancementState {
39+
private final String className;
40+
private final ClassFileLocator.Resolution classFileResolution;
41+
private TypePool.Resolution typePoolResolution;
42+
43+
public EnhancementState(String className, ClassFileLocator.Resolution classFileResolution) {
44+
this.className = className;
45+
this.classFileResolution = classFileResolution;
46+
}
47+
48+
public String getClassName() {
49+
return className;
50+
}
51+
52+
public ClassFileLocator.Resolution getClassFileResolution() {
53+
return classFileResolution;
54+
}
55+
56+
public TypePool.Resolution getTypePoolResolution() {
57+
return typePoolResolution;
58+
}
59+
60+
public void setTypePoolResolution(TypePool.Resolution typePoolResolution) {
61+
this.typePoolResolution = typePoolResolution;
62+
}
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.bytecode.enhance.internal.bytebuddy;
6+
7+
import net.bytebuddy.dynamic.ClassFileLocator;
8+
9+
import java.io.IOException;
10+
11+
/**
12+
* A delegating ClassFileLocator that allows overriding the resolution for the class that is currently being enhanced.
13+
*/
14+
final class EnhancerClassFileLocator implements ClassFileLocator {
15+
16+
private final EnhancerCacheProvider cacheProvider;
17+
private final ClassFileLocator delegate;
18+
19+
public EnhancerClassFileLocator(EnhancerCacheProvider cacheProvider, ClassFileLocator delegate) {
20+
this.cacheProvider = cacheProvider;
21+
this.delegate = delegate;
22+
}
23+
24+
@Override
25+
public Resolution locate(final String name) throws IOException {
26+
final EnhancerCacheProvider.EnhancementState enhancementState = cacheProvider.getEnhancementState();
27+
if ( enhancementState != null && enhancementState.getClassName().equals( name ) ) {
28+
return enhancementState.getClassFileResolution();
29+
}
30+
return delegate.locate( name );
31+
}
32+
33+
@Override
34+
public void close() throws IOException {
35+
delegate.close();
36+
}
37+
38+
}

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ModelTypePool.java

+21-32
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,21 @@
44
*/
55
package org.hibernate.bytecode.enhance.internal.bytebuddy;
66

7-
import java.util.Objects;
8-
import java.util.concurrent.ConcurrentHashMap;
9-
107
import net.bytebuddy.dynamic.ClassFileLocator;
118
import net.bytebuddy.pool.TypePool;
129

10+
import java.util.Objects;
11+
1312
/**
1413
* A TypePool suitable for loading user's classes,
1514
* potentially in parallel operations.
1615
*/
1716
public class ModelTypePool extends TypePool.Default implements EnhancerClassLocator {
1817

19-
private final ConcurrentHashMap<String, Resolution> resolutions = new ConcurrentHashMap<>();
20-
private final OverridingClassFileLocator locator;
21-
private final SafeCacheProvider poolCache;
18+
private final EnhancerClassFileLocator locator;
19+
private final EnhancerCacheProvider poolCache;
2220

23-
private ModelTypePool(SafeCacheProvider cacheProvider, OverridingClassFileLocator classFileLocator, CoreTypePool parent) {
21+
private ModelTypePool(EnhancerCacheProvider cacheProvider, EnhancerClassFileLocator classFileLocator, CoreTypePool parent) {
2422
super( cacheProvider, classFileLocator, ReaderMode.FAST, parent );
2523
this.poolCache = cacheProvider;
2624
this.locator = classFileLocator;
@@ -62,7 +60,7 @@ public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFile
6260
* @return
6361
*/
6462
public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool) {
65-
return buildModelTypePool( classFileLocator, coreTypePool, new SafeCacheProvider() );
63+
return buildModelTypePool( classFileLocator, coreTypePool, new EnhancerCacheProvider() );
6664
}
6765

6866
/**
@@ -72,44 +70,35 @@ public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFile
7270
* @param cacheProvider
7371
* @return
7472
*/
75-
public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool, SafeCacheProvider cacheProvider) {
73+
static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool, EnhancerCacheProvider cacheProvider) {
7674
Objects.requireNonNull( classFileLocator );
7775
Objects.requireNonNull( coreTypePool );
7876
Objects.requireNonNull( cacheProvider );
79-
return new ModelTypePool( cacheProvider, new OverridingClassFileLocator( classFileLocator ), coreTypePool );
80-
}
81-
82-
@Override
83-
protected Resolution doDescribe(final String name) {
84-
final Resolution resolution = resolutions.get( name );
85-
if ( resolution != null ) {
86-
return resolution;
87-
}
88-
else {
89-
return resolutions.computeIfAbsent( name, super::doDescribe );
90-
}
77+
return new ModelTypePool( cacheProvider, new EnhancerClassFileLocator( cacheProvider, classFileLocator ), coreTypePool );
9178
}
9279

9380
@Override
9481
public void registerClassNameAndBytes(final String className, final byte[] bytes) {
95-
//Very important: ensure the registered override is actually effective in case this class
96-
//was already resolved in the recent past; this could have happened for example as a side effect
97-
//of symbol resolution during enhancement of a different class, or very simply when attempting
98-
//to re-enhanced the same class - which happens frequently in WildFly because of the class transformers
99-
//being triggered concurrently by multiple parallel deployments.
100-
resolutions.remove( className );
101-
poolCache.remove( className );
102-
locator.put( className, new ClassFileLocator.Resolution.Explicit( Objects.requireNonNull( bytes ) ) );
82+
final EnhancerCacheProvider.EnhancementState currentEnhancementState = poolCache.getEnhancementState();
83+
if ( currentEnhancementState != null ) {
84+
throw new IllegalStateException( "Re-entrant enhancement is not supported: " + className );
85+
}
86+
final EnhancerCacheProvider.EnhancementState state = new EnhancerCacheProvider.EnhancementState(
87+
className,
88+
new ClassFileLocator.Resolution.Explicit( Objects.requireNonNull( bytes ) )
89+
);
90+
// Set the state first because the ClassFileLocator needs this in the doDescribe() call below
91+
poolCache.setEnhancementState( state );
92+
state.setTypePoolResolution( doDescribe( className ) );
10393
}
10494

10595
@Override
106-
public void deregisterClassNameAndBytes(final String className) {
107-
locator.remove( className );
96+
public void deregisterClassNameAndBytes(String className) {
97+
poolCache.removeEnhancementState();
10898
}
10999

110100
@Override
111101
public ClassFileLocator asClassFileLocator() {
112102
return locator;
113103
}
114-
115104
}

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/OverridingClassFileLocator.java

-80
This file was deleted.

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/SafeCacheProvider.java

-47
This file was deleted.

hibernate-core/src/main/java/org/hibernate/jpa/internal/enhance/EnhancingClassTransformerImpl.java

-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,6 @@ public byte[] transform(
6060
catch (final Exception e) {
6161
throw new TransformerException( "Error performing enhancement of " + className, e );
6262
}
63-
finally {
64-
bytecodeProvider.resetCaches();
65-
}
6663
}
6764

6865
@Override

0 commit comments

Comments
 (0)