From b5221e2ec65d0118fb04fcbc496e599066014b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 8 Nov 2024 15:11:58 +0100 Subject: [PATCH] HHH-18833 Introduce EnhancementContext#getUnsupportedEnhancementStrategy This method allows custom contexts to pick the behavior they want when a class contains getters/setters that do not have a matching field, making enhancement impossible. Three behaviors are available: * SKIP (the default), which will skip enhancement of such classes. * FAIL, which will throw an exception upon encountering such classes. * LEGACY, which will restore the pre-HHH-16572 behavior. I do not think LEGACY is useful at the moment, but I wanted to have that option in case it turns out HHH-16572 does more harm than good in Quarkus 3.15. --- .../ByteBuddyEnhancementContext.java | 5 + .../internal/bytebuddy/EnhancerImpl.java | 45 ++++-- .../enhance/spi/EnhancementContext.java | 12 ++ .../spi/UnsupportedEnhancementStrategy.java | 38 +++++ .../UnsupportedEnhancementStrategyTest.java | 131 ++++++++++++++++++ 5 files changed, 223 insertions(+), 8 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/UnsupportedEnhancementStrategy.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/access/UnsupportedEnhancementStrategyTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ByteBuddyEnhancementContext.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ByteBuddyEnhancementContext.java index 46c3acca09..99288dcdc4 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ByteBuddyEnhancementContext.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ByteBuddyEnhancementContext.java @@ -22,6 +22,7 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.scaffold.MethodGraph; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.pool.TypePool; +import org.hibernate.bytecode.enhance.spi.UnsupportedEnhancementStrategy; import static net.bytebuddy.matcher.ElementMatchers.isGetter; @@ -94,6 +95,10 @@ class ByteBuddyEnhancementContext { enhancementContext.registerDiscoveredType( new UnloadedTypeDescription( typeDescription ), type ); } + public UnsupportedEnhancementStrategy getUnsupportedEnhancementStrategy() { + return enhancementContext.getUnsupportedEnhancementStrategy(); + } + public void discoverCompositeTypes(TypeDescription managedCtClass, TypePool typePool) { if ( isDiscoveredType( managedCtClass ) ) { return; diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java index 547b1895ab..36310ab84e 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java @@ -23,6 +23,7 @@ import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.StubMethod; +import org.hibernate.AssertionFailure; import org.hibernate.Version; import org.hibernate.bytecode.enhance.VersionMismatchException; import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker; @@ -33,6 +34,7 @@ import org.hibernate.bytecode.enhance.spi.EnhancementInfo; import org.hibernate.bytecode.enhance.spi.Enhancer; import org.hibernate.bytecode.enhance.spi.EnhancerConstants; import org.hibernate.bytecode.enhance.spi.UnloadedField; +import org.hibernate.bytecode.enhance.spi.UnsupportedEnhancementStrategy; import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor; import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState; import org.hibernate.engine.spi.CompositeOwner; @@ -171,7 +173,7 @@ public class EnhancerImpl implements Enhancer { } if ( enhancementContext.isEntityClass( managedCtClass ) ) { - if ( hasUnsupportedAttributeNaming( managedCtClass ) ) { + if ( checkUnsupportedAttributeNaming( managedCtClass ) ) { // do not enhance classes with mismatched names for PROPERTY-access persistent attributes return null; } @@ -335,7 +337,7 @@ public class EnhancerImpl implements Enhancer { return createTransformer( managedCtClass ).applyTo( builder ); } else if ( enhancementContext.isCompositeClass( managedCtClass ) ) { - if ( hasUnsupportedAttributeNaming( managedCtClass ) ) { + if ( checkUnsupportedAttributeNaming( managedCtClass ) ) { // do not enhance classes with mismatched names for PROPERTY-access persistent attributes return null; } @@ -375,7 +377,7 @@ public class EnhancerImpl implements Enhancer { else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) { // Check for HHH-16572 (PROPERTY attributes with mismatched field and method names) - if ( hasUnsupportedAttributeNaming( managedCtClass ) ) { + if ( checkUnsupportedAttributeNaming( managedCtClass ) ) { return null; } @@ -399,8 +401,22 @@ public class EnhancerImpl implements Enhancer { * Check whether an entity class ({@code managedCtClass}) has mismatched names between a persistent field and its * getter/setter when using {@link AccessType#PROPERTY}, which Hibernate does not currently support for enhancement. * See https://hibernate.atlassian.net/browse/HHH-16572 + * + * @return {@code true} if enhancement of the class must be {@link org.hibernate.bytecode.enhance.spi.UnsupportedEnhancementStrategy#SKIP skipped} + * because it has mismatched names. + * {@code false} if enhancement of the class must proceed, either because it doesn't have any mismatched names, + * or because {@link org.hibernate.bytecode.enhance.spi.UnsupportedEnhancementStrategy#LEGACY legacy mode} was opted into. + * @throws EnhancementException if enhancement of the class must {@link org.hibernate.bytecode.enhance.spi.UnsupportedEnhancementStrategy#FAIL abort} because it has mismatched names. */ - private boolean hasUnsupportedAttributeNaming(TypeDescription managedCtClass) { + @SuppressWarnings("deprecation") + private boolean checkUnsupportedAttributeNaming(TypeDescription managedCtClass) { + var strategy = enhancementContext.getUnsupportedEnhancementStrategy(); + if ( UnsupportedEnhancementStrategy.LEGACY.equals( strategy ) ) { + // Don't check anything and act as if there was nothing unsupported in the class. + // This is unsafe but that's what LEGACY is about. + return false; + } + // For process access rules, See https://jakarta.ee/specifications/persistence/3.2/jakarta-persistence-spec-3.2#default-access-type // and https://jakarta.ee/specifications/persistence/3.2/jakarta-persistence-spec-3.2#a122 // @@ -462,10 +478,23 @@ public class EnhancerImpl implements Enhancer { } } } - if (propertyHasAnnotation && !propertyNameMatchesFieldName) { - log.debugf("Skipping enhancement of [%s]: due to class [%s] not having a property accessor method name matching field name [%s]", - managedCtClass, methodDescription.getDeclaringType().getActualName(), methodFieldName); - return true; + if ( propertyHasAnnotation && !propertyNameMatchesFieldName ) { + switch ( strategy ) { + case SKIP: + log.debugf( + "Skipping enhancement of [%s] because no field named [%s] could be found for property accessor method [%s]." + + " To fix this, make sure all property accessor methods have a matching field.", + managedCtClass.getName(), methodFieldName, methodDescription.getName() ); + return true; + case FAIL: + throw new EnhancementException( String.format( + "Enhancement of [%s] failed because no field named [%s] could be found for property accessor method [%s]." + + " To fix this, make sure all property accessor methods have a matching field.", + managedCtClass.getName(), methodFieldName, methodDescription.getName() ) ); + default: + // We shouldn't even be in this method if using LEGACY, see top of this method. + throw new AssertionFailure( "Unexpected strategy at this point: " + strategy ); + } } } return false; diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/EnhancementContext.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/EnhancementContext.java index 7a68e2bebc..97bef960b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/EnhancementContext.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/EnhancementContext.java @@ -5,6 +5,7 @@ package org.hibernate.bytecode.enhance.spi; import jakarta.persistence.metamodel.Type; +import org.hibernate.Incubating; /** * The context for performing an enhancement. Enhancement can happen in any number of ways: