-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
5 changed files
with
225 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
...core/src/main/java/org/hibernate/bytecode/enhance/spi/UnsupportedEnhancementStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* | ||
* Hibernate, Relational Persistence for Idiomatic Java | ||
* | ||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. | ||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. | ||
*/ | ||
package org.hibernate.bytecode.enhance.spi; | ||
|
||
import org.hibernate.Incubating; | ||
|
||
/** | ||
* The expected behavior when encountering a class that cannot be enhanced, | ||
* in particular when attribute names don't match field names. | ||
* | ||
* @see org.hibernate.bytecode.enhance.spi.EnhancementContext#getUnsupportedEnhancementStrategy | ||
*/ | ||
@Incubating | ||
public enum UnsupportedEnhancementStrategy { | ||
|
||
/** | ||
* When a class cannot be enhanced, skip enhancement for that class only. | ||
*/ | ||
SKIP, | ||
/** | ||
* When a class cannot be enhanced, throw an exception with an actionable message. | ||
*/ | ||
FAIL, | ||
/** | ||
* Legacy behavior: when a class cannot be enhanced, ignore that fact and try to enhance it anyway. | ||
* <p> | ||
* <strong>This is utterly unsafe and may cause errors, unpredictable behavior, and data loss.</strong> | ||
* <p> | ||
* Intended only for internal use in contexts with rigid backwards compatibility requirements. | ||
* | ||
* @deprecated Use {@link #SKIP} or {@link #FAIL} instead. | ||
*/ | ||
@Deprecated | ||
LEGACY | ||
|
||
} |
131 changes: 131 additions & 0 deletions
131
...rg/hibernate/orm/test/bytecode/enhancement/access/UnsupportedEnhancementStrategyTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/* | ||
* SPDX-License-Identifier: LGPL-2.1-or-later | ||
* Copyright Red Hat Inc. and Hibernate Authors | ||
*/ | ||
package org.hibernate.orm.test.bytecode.enhancement.access; | ||
|
||
import jakarta.persistence.Access; | ||
import jakarta.persistence.AccessType; | ||
import jakarta.persistence.Basic; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.Table; | ||
import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImpl; | ||
import org.hibernate.bytecode.enhance.spi.EnhancementContext; | ||
import org.hibernate.bytecode.enhance.spi.EnhancementException; | ||
import org.hibernate.bytecode.enhance.spi.Enhancer; | ||
import org.hibernate.bytecode.enhance.spi.UnsupportedEnhancementStrategy; | ||
import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState; | ||
import org.hibernate.bytecode.spi.ByteCodeHelper; | ||
import org.hibernate.testing.bytecode.enhancement.EnhancerTestContext; | ||
import org.hibernate.testing.orm.junit.JiraKey; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
@JiraKey("HHH-18833") | ||
public class UnsupportedEnhancementStrategyTest { | ||
|
||
@Test | ||
public void skip() throws IOException { | ||
var context = new EnhancerTestContext() { | ||
@Override | ||
public UnsupportedEnhancementStrategy getUnsupportedEnhancementStrategy() { | ||
// This is currently the default, but we don't care about what's the default here | ||
return UnsupportedEnhancementStrategy.SKIP; | ||
} | ||
}; | ||
byte[] originalBytes = getAsBytes( SomeEntity.class ); | ||
byte[] enhancedBytes = doEnhance( SomeEntity.class, originalBytes, context ); | ||
assertThat( enhancedBytes ).isNull(); // null means "not enhanced" | ||
} | ||
|
||
@Test | ||
public void fail() throws IOException { | ||
var context = new EnhancerTestContext() { | ||
@Override | ||
public UnsupportedEnhancementStrategy getUnsupportedEnhancementStrategy() { | ||
return UnsupportedEnhancementStrategy.FAIL; | ||
} | ||
}; | ||
byte[] originalBytes = getAsBytes( SomeEntity.class ); | ||
assertThatThrownBy( () -> doEnhance( SomeEntity.class, originalBytes, context ) ) | ||
.isInstanceOf( EnhancementException.class ) | ||
.hasMessageContainingAll( | ||
String.format( | ||
"Enhancement of [%s] failed because no field named [%s] could be found for property accessor method [%s].", | ||
SomeEntity.class.getName(), "propertyMethod", "getPropertyMethod" ), | ||
"To fix this, make sure all property accessor methods have a matching field." | ||
); | ||
} | ||
|
||
@Test | ||
@SuppressWarnings("deprecation") | ||
public void legacy() throws IOException { | ||
var context = new EnhancerTestContext() { | ||
@Override | ||
public UnsupportedEnhancementStrategy getUnsupportedEnhancementStrategy() { | ||
// This is currently the default, but we don't care about what's the default here | ||
return UnsupportedEnhancementStrategy.LEGACY; | ||
} | ||
}; | ||
byte[] originalBytes = getAsBytes( SomeEntity.class ); | ||
byte[] enhancedBytes = doEnhance( SomeEntity.class, originalBytes, context ); | ||
assertThat( enhancedBytes ).isNotNull(); // non-null means enhancement _was_ performed | ||
} | ||
|
||
private byte[] doEnhance(Class<SomeEntity> someEntityClass, byte[] originalBytes, EnhancementContext context) { | ||
final ByteBuddyState byteBuddyState = new ByteBuddyState(); | ||
final Enhancer enhancer = new EnhancerImpl( context, byteBuddyState ); | ||
return enhancer.enhance( someEntityClass.getName(), originalBytes ); | ||
} | ||
|
||
private byte[] getAsBytes(Class<?> clazz) throws IOException { | ||
final String classFile = clazz.getName().replace( '.', '/' ) + ".class"; | ||
try (InputStream classFileStream = clazz.getClassLoader().getResourceAsStream( classFile )) { | ||
return ByteCodeHelper.readByteCode( classFileStream ); | ||
} | ||
} | ||
|
||
@Entity | ||
@Table(name = "SOME_ENTITY") | ||
static class SomeEntity { | ||
@Id | ||
Long id; | ||
|
||
@Basic | ||
String field; | ||
|
||
String property; | ||
|
||
public SomeEntity() { | ||
} | ||
|
||
public SomeEntity(Long id, String field, String property) { | ||
this.id = id; | ||
this.field = field; | ||
this.property = property; | ||
} | ||
|
||
/** | ||
* The following property accessor methods are purposely named incorrectly to | ||
* not match the "property" field. The HHH-16572 change ensures that | ||
* this entity is not (bytecode) enhanced. Eventually further changes will be made | ||
* such that this entity is enhanced in which case the FailureExpected can be removed | ||
* and the cleanup() uncommented. | ||
*/ | ||
@Basic | ||
@Access(AccessType.PROPERTY) | ||
public String getPropertyMethod() { | ||
return "from getter: " + property; | ||
} | ||
|
||
public void setPropertyMethod(String property) { | ||
this.property = property; | ||
} | ||
} | ||
} |