Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HHH-14694 Use stable proxy names to avoid managing proxy state #9831

Merged
merged 4 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.bytecode.enhance.internal.bytebuddy;

import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.pool.TypePool;

/**
* A simple cache provider that allows overriding the resolution for the class that is currently being enhanced.
*/
final class EnhancerCacheProvider extends TypePool.CacheProvider.Simple {

private final ThreadLocal<EnhancementState> enhancementState = new ThreadLocal<>();

@Override
public TypePool.Resolution find(final String name) {
final EnhancementState enhancementState = getEnhancementState();
if ( enhancementState != null && enhancementState.getClassName().equals( name ) ) {
return enhancementState.getTypePoolResolution();
}
return super.find( name );
}

EnhancementState getEnhancementState() {
return enhancementState.get();
}

void setEnhancementState(EnhancementState state) {
enhancementState.set( state );
}

void removeEnhancementState() {
enhancementState.remove();
}

static final class EnhancementState {
private final String className;
private final ClassFileLocator.Resolution classFileResolution;
private TypePool.Resolution typePoolResolution;

public EnhancementState(String className, ClassFileLocator.Resolution classFileResolution) {
this.className = className;
this.classFileResolution = classFileResolution;
}

public String getClassName() {
return className;
}

public ClassFileLocator.Resolution getClassFileResolution() {
return classFileResolution;
}

public TypePool.Resolution getTypePoolResolution() {
return typePoolResolution;
}

public void setTypePoolResolution(TypePool.Resolution typePoolResolution) {
this.typePoolResolution = typePoolResolution;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.bytecode.enhance.internal.bytebuddy;

import net.bytebuddy.dynamic.ClassFileLocator;

import java.io.IOException;

/**
* A delegating ClassFileLocator that allows overriding the resolution for the class that is currently being enhanced.
*/
final class EnhancerClassFileLocator implements ClassFileLocator {

private final EnhancerCacheProvider cacheProvider;
private final ClassFileLocator delegate;

public EnhancerClassFileLocator(EnhancerCacheProvider cacheProvider, ClassFileLocator delegate) {
this.cacheProvider = cacheProvider;
this.delegate = delegate;
}

@Override
public Resolution locate(final String name) throws IOException {
final EnhancerCacheProvider.EnhancementState enhancementState = cacheProvider.getEnhancementState();
if ( enhancementState != null && enhancementState.getClassName().equals( name ) ) {
return enhancementState.getClassFileResolution();
}
return delegate.locate( name );
}

@Override
public void close() throws IOException {
delegate.close();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,21 @@
*/
package org.hibernate.bytecode.enhance.internal.bytebuddy;

import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.pool.TypePool;

import java.util.Objects;

/**
* A TypePool suitable for loading user's classes,
* potentially in parallel operations.
*/
public class ModelTypePool extends TypePool.Default implements EnhancerClassLocator {

private final ConcurrentHashMap<String, Resolution> resolutions = new ConcurrentHashMap<>();
private final OverridingClassFileLocator locator;
private final SafeCacheProvider poolCache;
private final EnhancerClassFileLocator locator;
private final EnhancerCacheProvider poolCache;

private ModelTypePool(SafeCacheProvider cacheProvider, OverridingClassFileLocator classFileLocator, CoreTypePool parent) {
private ModelTypePool(EnhancerCacheProvider cacheProvider, EnhancerClassFileLocator classFileLocator, CoreTypePool parent) {
super( cacheProvider, classFileLocator, ReaderMode.FAST, parent );
this.poolCache = cacheProvider;
this.locator = classFileLocator;
Expand Down Expand Up @@ -62,7 +60,7 @@ public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFile
* @return
*/
public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool) {
return buildModelTypePool( classFileLocator, coreTypePool, new SafeCacheProvider() );
return buildModelTypePool( classFileLocator, coreTypePool, new EnhancerCacheProvider() );
}

/**
Expand All @@ -72,44 +70,35 @@ public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFile
* @param cacheProvider
* @return
*/
public static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool, SafeCacheProvider cacheProvider) {
static EnhancerClassLocator buildModelTypePool(ClassFileLocator classFileLocator, CoreTypePool coreTypePool, EnhancerCacheProvider cacheProvider) {
Objects.requireNonNull( classFileLocator );
Objects.requireNonNull( coreTypePool );
Objects.requireNonNull( cacheProvider );
return new ModelTypePool( cacheProvider, new OverridingClassFileLocator( classFileLocator ), coreTypePool );
}

@Override
protected Resolution doDescribe(final String name) {
final Resolution resolution = resolutions.get( name );
if ( resolution != null ) {
return resolution;
}
else {
return resolutions.computeIfAbsent( name, super::doDescribe );
}
return new ModelTypePool( cacheProvider, new EnhancerClassFileLocator( cacheProvider, classFileLocator ), coreTypePool );
}

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

@Override
public void deregisterClassNameAndBytes(final String className) {
locator.remove( className );
public void deregisterClassNameAndBytes(String className) {
poolCache.removeEnhancementState();
}

@Override
public ClassFileLocator asClassFileLocator() {
return locator;
}

}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.proxy.ProxyConfiguration;

import net.bytebuddy.NamingStrategy;
import net.bytebuddy.TypeCache;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;

Expand All @@ -37,13 +35,12 @@ public BasicProxyFactoryImpl(final Class superClass, final Class interfaceClass,
}

final Class<?> superClassOrMainInterface = superClass != null ? superClass : interfaceClass;
final TypeCache.SimpleKey cacheKey = new TypeCache.SimpleKey( superClassOrMainInterface );
final ByteBuddyState.ProxyDefinitionHelpers helpers = byteBuddyState.getProxyDefinitionHelpers();
final String proxyClassName = superClassOrMainInterface.getName() + "$" + PROXY_NAMING_SUFFIX;

ByteBuddyState.ProxyDefinitionHelpers helpers = byteBuddyState.getProxyDefinitionHelpers();

this.proxyClass = byteBuddyState.loadBasicProxy( superClassOrMainInterface, cacheKey, byteBuddy ->
this.proxyClass = byteBuddyState.loadBasicProxy( superClassOrMainInterface, proxyClassName, (byteBuddy, namingStrategy) ->
helpers.appendIgnoreAlsoAtEnd( byteBuddy
.with( new NamingStrategy.SuffixingRandom( PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( superClassOrMainInterface.getName() ) ) )
.with( namingStrategy )
.subclass( superClass == null ? Object.class : superClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR )
.implement( interfaceClass == null ? NO_INTERFACES : new Class[]{ interfaceClass } )
.defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE )
Expand Down
Loading
Loading