Skip to content

Commit e8073d1

Browse files
authored
Merge pull request #49579 from gsmet/hibernate-proxy-improvements
Hibernate ORM - Assorted improvements to proxy generation
2 parents c31de20 + 8bd0f55 commit e8073d1

File tree

3 files changed

+64
-39
lines changed

3 files changed

+64
-39
lines changed

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateEntityEnhancer.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public final class HibernateEntityEnhancer implements BiFunction<String, ClassVi
3636
ClassFileVersion.JAVA_V17);
3737

3838
//Choose this set to include Jakarta annotations, basic Java types such as String and Map, Hibernate annotations, and Panache supertypes:
39-
private static final CoreTypePool CORE_POOL = new CoreTypePool(
39+
static final CoreTypePool CORE_TYPE_POOL = new CoreTypePool(
4040
"java.",
4141
"jakarta.",
4242
"org.hibernate.bytecode.enhance.spi.",
@@ -64,6 +64,7 @@ public HibernateEnhancingClassVisitor(String className, ClassVisitor outputClass
6464
//Careful: the ASM API version needs to match the ASM version of Gizmo, not the one from Byte Buddy.
6565
//Most often these match - but occasionally they will diverge which is acceptable as Byte Buddy is shading ASM.
6666
super(Gizmo.ASM_API_VERSION, new QuarkusClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS));
67+
6768
this.className = className;
6869
this.outputClassVisitor = outputClassVisitor;
6970
this.enhancerHolder = enhancerHolder;
@@ -103,7 +104,7 @@ public Enhancer getEnhancer() {
103104
synchronized (this) {
104105
if (actualEnhancer == null) {
105106
EnhancerClassLocator enhancerClassLocator = ModelTypePool
106-
.buildModelTypePool(QuarkusClassFileLocator.INSTANCE, CORE_POOL);
107+
.buildModelTypePool(QuarkusClassFileLocator.INSTANCE, CORE_TYPE_POOL);
107108
actualEnhancer = PROVIDER.getEnhancer(QuarkusEnhancementContext.INSTANCE, enhancerClassLocator);
108109
}
109110
}

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
import java.util.Properties;
3030
import java.util.Set;
3131
import java.util.TreeSet;
32+
import java.util.concurrent.CompletableFuture;
33+
import java.util.concurrent.ConcurrentLinkedDeque;
34+
import java.util.concurrent.ExecutionException;
35+
import java.util.concurrent.ExecutorService;
36+
import java.util.concurrent.Future;
3237
import java.util.function.BiConsumer;
3338
import java.util.stream.Collectors;
3439

@@ -111,6 +116,7 @@
111116
import io.quarkus.hibernate.orm.PersistenceUnit;
112117
import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem;
113118
import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationStaticConfiguredBuildItem;
119+
import io.quarkus.hibernate.orm.deployment.integration.QuarkusClassFileLocator;
114120
import io.quarkus.hibernate.orm.deployment.spi.AdditionalJpaModelBuildItem;
115121
import io.quarkus.hibernate.orm.deployment.spi.DatabaseKindDialectBuildItem;
116122
import io.quarkus.hibernate.orm.dev.HibernateOrmDevIntegrator;
@@ -144,6 +150,8 @@
144150
import net.bytebuddy.dynamic.ClassFileLocator;
145151
import net.bytebuddy.dynamic.DynamicType;
146152
import net.bytebuddy.pool.TypePool;
153+
import net.bytebuddy.pool.TypePool.CacheProvider;
154+
import net.bytebuddy.pool.TypePool.Default.ReaderMode;
147155

148156
/**
149157
* Simulacrum of JPA bootstrap.
@@ -462,7 +470,8 @@ public BytecodeRecorderConstantDefinitionBuildItem pregenProxies(
462470
List<PersistenceUnitDescriptorBuildItem> persistenceUnitDescriptorBuildItems,
463471
List<AdditionalJpaModelBuildItem> additionalJpaModelBuildItems,
464472
BuildProducer<GeneratedClassBuildItem> generatedClassBuildItemBuildProducer,
465-
LiveReloadBuildItem liveReloadBuildItem) {
473+
LiveReloadBuildItem liveReloadBuildItem,
474+
ExecutorService buildExecutor) throws ExecutionException, InterruptedException {
466475
Set<String> managedClassAndPackageNames = new HashSet<>(jpaModel.getEntityClassNames());
467476
for (PersistenceUnitDescriptorBuildItem pud : persistenceUnitDescriptorBuildItems) {
468477
// Note: getManagedClassNames() can also return *package* names
@@ -476,9 +485,9 @@ public BytecodeRecorderConstantDefinitionBuildItem pregenProxies(
476485
managedClassAndPackageNames.add(additionalJpaModelBuildItem.getClassName());
477486
}
478487

479-
PreGeneratedProxies proxyDefinitions = generatedProxies(managedClassAndPackageNames,
488+
PreGeneratedProxies proxyDefinitions = generateProxies(managedClassAndPackageNames,
480489
indexBuildItem.getIndex(), transformedClassesBuildItem,
481-
generatedClassBuildItemBuildProducer, liveReloadBuildItem);
490+
generatedClassBuildItemBuildProducer, liveReloadBuildItem, buildExecutor);
482491

483492
// Make proxies available through a constant;
484493
// this is a hack to avoid introducing circular dependencies between build steps.
@@ -1369,53 +1378,65 @@ private static MultiTenancyStrategy getMultiTenancyStrategy(Optional<String> mul
13691378
return multiTenancyStrategy;
13701379
}
13711380

1372-
private PreGeneratedProxies generatedProxies(Set<String> managedClassAndPackageNames, IndexView combinedIndex,
1381+
private PreGeneratedProxies generateProxies(Set<String> managedClassAndPackageNames, IndexView combinedIndex,
13731382
TransformedClassesBuildItem transformedClassesBuildItem,
13741383
BuildProducer<GeneratedClassBuildItem> generatedClassBuildItemBuildProducer,
1375-
LiveReloadBuildItem liveReloadBuildItem) {
1384+
LiveReloadBuildItem liveReloadBuildItem,
1385+
ExecutorService buildExecutor) throws ExecutionException, InterruptedException {
13761386
ProxyCache proxyCache = liveReloadBuildItem.getContextObject(ProxyCache.class);
13771387
if (proxyCache == null) {
13781388
proxyCache = new ProxyCache();
13791389
liveReloadBuildItem.setContextObject(ProxyCache.class, proxyCache);
13801390
}
1381-
Set<String> changedClasses = Collections.emptySet();
1391+
Set<String> changedClasses = Set.of();
13821392
if (liveReloadBuildItem.getChangeInformation() != null) {
13831393
changedClasses = liveReloadBuildItem.getChangeInformation().getChangedClasses();
13841394
} else {
13851395
//we don't have class change info, invalidate the cache
13861396
proxyCache.cache.clear();
13871397
}
13881398
//create a map of entity to proxy type
1389-
PreGeneratedProxies preGeneratedProxies = new PreGeneratedProxies();
13901399
TypePool transformedClassesTypePool = createTransformedClassesTypePool(transformedClassesBuildItem,
13911400
managedClassAndPackageNames);
1401+
1402+
PreGeneratedProxies preGeneratedProxies = new PreGeneratedProxies();
1403+
13921404
try (ProxyBuildingHelper proxyHelper = new ProxyBuildingHelper(transformedClassesTypePool)) {
1405+
final ConcurrentLinkedDeque<Future<CachedProxy>> generatedProxyQueue = new ConcurrentLinkedDeque<>();
1406+
Set<String> proxyInterfaceNames = Set.of(ClassNames.HIBERNATE_PROXY.toString());
1407+
13931408
for (String managedClassOrPackageName : managedClassAndPackageNames) {
1394-
CachedProxy result;
13951409
if (proxyCache.cache.containsKey(managedClassOrPackageName)
13961410
&& !isModified(managedClassOrPackageName, changedClasses, combinedIndex)) {
1397-
result = proxyCache.cache.get(managedClassOrPackageName);
1411+
CachedProxy proxy = proxyCache.cache.get(managedClassOrPackageName);
1412+
generatedProxyQueue.add(CompletableFuture.completedFuture(proxy));
13981413
} else {
1399-
Set<String> proxyInterfaceNames = new TreeSet<>();
1400-
proxyInterfaceNames.add(ClassNames.HIBERNATE_PROXY.toString()); //always added
1401-
if (!proxyHelper.isProxiable(managedClassOrPackageName)) {
1402-
// we need to make sure the actual class is proxiable
1414+
if (!proxyHelper.isProxiable(combinedIndex.getClassByName(managedClassOrPackageName))) {
1415+
// we need to make sure we have a class and not a package and that it is proxiable
14031416
continue;
14041417
}
1405-
final String mappedClass = managedClassOrPackageName;
1406-
DynamicType.Unloaded<?> unloaded = proxyHelper.buildUnloadedProxy(mappedClass, proxyInterfaceNames);
1407-
result = new CachedProxy(unloaded, proxyInterfaceNames);
1408-
proxyCache.cache.put(managedClassOrPackageName, result);
1418+
// we now are sure we have a proper class and not a package, let's avoid the confusion
1419+
String managedClass = managedClassOrPackageName;
1420+
generatedProxyQueue.add(buildExecutor.submit(() -> {
1421+
DynamicType.Unloaded<?> unloaded = proxyHelper.buildUnloadedProxy(managedClass,
1422+
proxyInterfaceNames);
1423+
return new CachedProxy(managedClass, unloaded, proxyInterfaceNames);
1424+
}));
14091425
}
1410-
for (Entry<TypeDescription, byte[]> i : result.proxyDef.getAllTypes().entrySet()) {
1426+
}
1427+
1428+
for (Future<CachedProxy> proxyFuture : generatedProxyQueue) {
1429+
CachedProxy proxy = proxyFuture.get();
1430+
proxyCache.cache.put(proxy.managedClassName, proxy);
1431+
for (Entry<TypeDescription, byte[]> i : proxy.proxyDef.getAllTypes().entrySet()) {
14111432
generatedClassBuildItemBuildProducer
14121433
.produce(new GeneratedClassBuildItem(true, i.getKey().getName(), i.getValue()));
14131434
}
1414-
preGeneratedProxies.getProxies().put(managedClassOrPackageName,
1415-
new PreGeneratedProxies.ProxyClassDetailsHolder(result.proxyDef.getTypeDescription().getName(),
1416-
result.interfaces));
1435+
preGeneratedProxies.getProxies().put(proxy.managedClassName, new PreGeneratedProxies.ProxyClassDetailsHolder(
1436+
proxy.proxyDef.getTypeDescription().getName(), proxy.interfaces));
14171437
}
14181438
}
1439+
14191440
return preGeneratedProxies;
14201441
}
14211442

@@ -1435,9 +1456,15 @@ private TypePool createTransformedClassesTypePool(TransformedClassesBuildItem tr
14351456
}
14361457
}
14371458
}
1438-
return TypePool.Default.of(new ClassFileLocator.Compound(
1459+
1460+
ClassFileLocator classFileLocator = new ClassFileLocator.Compound(
14391461
new ClassFileLocator.Simple(transformedClasses),
1440-
ClassFileLocator.ForClassLoader.of(Thread.currentThread().getContextClassLoader())));
1462+
QuarkusClassFileLocator.INSTANCE);
1463+
1464+
// we can reuse the core TypePool but we may not reuse the full enhancer TypePool
1465+
// or PublicFieldWithProxyAndLazyLoadingAndInheritanceTest will fail
1466+
return new TypePool.Default(new CacheProvider.Simple(), classFileLocator, ReaderMode.FAST,
1467+
HibernateEntityEnhancer.CORE_TYPE_POOL);
14411468
}
14421469

14431470
private boolean isModified(String entity, Set<String> changedClasses, IndexView index) {
@@ -1455,7 +1482,7 @@ private boolean isModified(String entity, Set<String> changedClasses, IndexView
14551482
}
14561483
}
14571484
DotName superName = clazz.superName();
1458-
if (superName != null) {
1485+
if (superName != null && !DotName.OBJECT_NAME.equals(superName)) {
14591486
return isModified(superName.toString(), changedClasses, index);
14601487
}
14611488
return false;
@@ -1467,10 +1494,12 @@ private static final class ProxyCache {
14671494
}
14681495

14691496
static final class CachedProxy {
1497+
final String managedClassName;
14701498
final DynamicType.Unloaded<?> proxyDef;
14711499
final Set<String> interfaces;
14721500

1473-
CachedProxy(DynamicType.Unloaded<?> proxyDef, Set<String> interfaces) {
1501+
CachedProxy(String managedClassName, DynamicType.Unloaded<?> proxyDef, Set<String> interfaces) {
1502+
this.managedClassName = managedClassName;
14741503
this.proxyDef = proxyDef;
14751504
this.interfaces = interfaces;
14761505
}

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/ProxyBuildingHelper.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66

77
import org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl;
88
import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper;
9+
import org.jboss.jandex.ClassInfo;
910

1011
import net.bytebuddy.ClassFileVersion;
1112
import net.bytebuddy.description.method.MethodDescription;
1213
import net.bytebuddy.description.type.TypeDefinition;
13-
import net.bytebuddy.description.type.TypeDescription;
1414
import net.bytebuddy.dynamic.DynamicType;
1515
import net.bytebuddy.matcher.ElementMatcher;
1616
import net.bytebuddy.matcher.ElementMatchers;
@@ -52,17 +52,12 @@ private ByteBuddyProxyHelper getByteBuddyProxyHelper() {
5252
return this.byteBuddyProxyHelper;
5353
}
5454

55-
public boolean isProxiable(String managedClassOrPackageName) {
56-
TypePool.Resolution mappedClassResolution = typePool.describe(managedClassOrPackageName);
57-
if (!mappedClassResolution.isResolved()) {
58-
// Probably a package name - consider it's not proxiable.
59-
return false;
60-
}
61-
62-
TypeDescription mappedClass = mappedClassResolution.resolve();
63-
64-
return !mappedClass.isFinal()
65-
&& !mappedClass.getDeclaredMethods().filter(NO_ARG_CONSTRUCTOR).isEmpty();
55+
public boolean isProxiable(ClassInfo classInfo) {
56+
return classInfo != null
57+
&& !classInfo.isInterface()
58+
&& !classInfo.isAbstract()
59+
&& !classInfo.isFinal()
60+
&& classInfo.hasNoArgsConstructor();
6661
}
6762

6863
@Override

0 commit comments

Comments
 (0)