fix issue with not compiling tests

This commit is contained in:
Andrea Boriero 2020-07-30 17:48:07 +01:00
parent 1a6b01a2a8
commit fc914ea647
44 changed files with 4557 additions and 12 deletions

View File

@ -32,6 +32,12 @@ sourceSets {
}
}
testJavassist {
java {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
}
}
}
configurations {
@ -39,10 +45,16 @@ configurations {
description = 'Configuration for the produced test jar'
}
//Configures the compile and runtime configurations for our javassist tests
//and includes the dependencies of the test task.
testJavassistCompile.extendsFrom testCompile
testJavassistRuntime.extendsFrom testRuntime
}
dependencies {
compile( libraries.jpa )
// This can now be made provided
compile( libraries.javassist )
compile( libraries.byteBuddy )
compile( libraries.antlr )
compile( libraries.jta )
@ -101,6 +113,7 @@ dependencies {
testRuntime( "org.jboss.spec.javax.ejb:jboss-ejb-api_3.2_spec:1.0.0.Final" )
testRuntime( libraries.expression_language )
testRuntime( 'jaxen:jaxen:1.1' )
testRuntime( libraries.javassist )
testRuntime( libraries.byteBuddy )
testRuntime( libraries.weld )
testRuntime( libraries.atomikos )
@ -115,6 +128,9 @@ dependencies {
testCompile libraries.jboss_ejb_spec_jar
testCompile libraries.jboss_annotation_spec_jar
// Additional tests requiring Javassist
// folder in src/javassist/java
testJavassistCompile libraries.javassist
}
jar {
@ -390,3 +406,15 @@ class Antlr4GenerationTask extends DefaultTask {
}
}
//Create the task that runs the integration tests found from the
//configured source directory and uses the correct classpath.
task testJavassist(type: Test) {
testClassesDirs = sourceSets.testJavassist.output.classesDirs
classpath = sourceSets.testJavassist.runtimeClasspath
//If you want to ensure that integration tests are run every time when you invoke
//this task, uncomment the following line.
//outputs.upToDateWhen { false }
}
check.dependsOn testJavassist
testJavassist.mustRunAfter test

View File

@ -0,0 +1,279 @@
/*
* 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.internal.javassist;
import java.util.Collection;
import java.util.Locale;
import java.util.Objects;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import javassist.CtClass;
import javassist.CtField;
import javassist.NotFoundException;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
/**
* utility class to generate interceptor methods
* @see org.hibernate.engine.spi.PersistentAttributeInterceptor
*
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
*/
public abstract class AttributeTypeDescriptor {
protected InheritanceMetadata inheritanceMetadata;
protected AttributeTypeDescriptor(InheritanceMetadata inheritanceMetadata) {
this.inheritanceMetadata = inheritanceMetadata;
}
public abstract String buildReadInterceptionBodyFragment(String fieldName);
public abstract String buildWriteInterceptionBodyFragment(String fieldName);
public String buildInLineDirtyCheckingBodyFragment(JavassistEnhancementContext context, CtField currentValue) {
StringBuilder builder = new StringBuilder();
try {
// should ignore primary keys
if ( PersistentAttributesHelper.hasAnnotation( currentValue, Id.class )
|| PersistentAttributesHelper.hasAnnotation( currentValue, EmbeddedId.class ) ) {
return "";
}
String readFragment = inheritanceMetadata.isInherited() && !inheritanceMetadata.isVisible()
? "super." + inheritanceMetadata.getReaderName() + "()"
: "this." + currentValue.getName();
if ( currentValue.getType().isPrimitive() || currentValue.getType().isEnum() ) {
// primitives || enums
builder.append( String.format( " if ( %s != $1 )", readFragment ) );
}
else {
// if the field is a collection we return since we handle that in a separate method
for ( CtClass ctClass : currentValue.getType().getInterfaces() ) {
if ( ctClass.getName().equals( Collection.class.getName() ) ) {
// if the collection is not managed we should write it to the tracker
if ( context.isMappedCollection( currentValue ) ) {
return "";
}
}
}
builder.append(
String.format(
" if ( !%s.deepEquals( %s, $1 ) )",
Objects.class.getName(),
readFragment
)
);
}
builder.append( String.format( " { %s(\"%s\"); }", EnhancerConstants.TRACKER_CHANGER_NAME, currentValue.getName() ) );
}
catch (NotFoundException ignore) {
}
return builder.toString();
}
/* --- */
/**
* factory method to get the AttributeTypeDescriptor for a particular field type
*/
public static AttributeTypeDescriptor resolve(CtClass managedCtClass, CtField persistentField) throws NotFoundException {
boolean inherited = !managedCtClass.equals( persistentField.getDeclaringClass() );
boolean visible = persistentField.visibleFrom( managedCtClass );
String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + persistentField.getName();
String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + persistentField.getName();
InheritanceMetadata inheritanceMetadata = new InheritanceMetadata( inherited, visible, readerName, writerName );
if ( CtClass.booleanType.equals( persistentField.getType() ) ) {
return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Boolean.TYPE );
}
else if ( CtClass.byteType.equals( persistentField.getType() )) {
return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Byte.TYPE );
}
else if ( CtClass.charType.equals( persistentField.getType() ) ) {
return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Character.TYPE );
}
else if ( CtClass.shortType.equals( persistentField.getType() ) ) {
return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Short.TYPE );
}
else if ( CtClass.intType.equals( persistentField.getType() ) ) {
return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Integer.TYPE );
}
else if ( CtClass.longType.equals( persistentField.getType() ) ) {
return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Long.TYPE );
}
else if ( CtClass.doubleType.equals( persistentField.getType() ) ) {
return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Double.TYPE );
}
else if ( CtClass.floatType.equals( persistentField.getType() ) ) {
return new PrimitiveAttributeTypeDescriptor( inheritanceMetadata, Float.TYPE );
}
else {
return new ObjectAttributeTypeDescriptor( inheritanceMetadata, persistentField.getType() );
}
}
/* --- */
/**
* AttributeTypeDescriptor for non primitive types
*/
private static class ObjectAttributeTypeDescriptor extends AttributeTypeDescriptor {
private final String type;
private ObjectAttributeTypeDescriptor(InheritanceMetadata inheritanceMetadata, CtClass concreteType) {
super( inheritanceMetadata );
this.type = concreteType.getName();
}
@Override
public String buildReadInterceptionBodyFragment(String fieldName) {
if ( inheritanceMetadata.isInherited() && !inheritanceMetadata.isVisible() ) {
return String.format(
" if( %3$s() != null ) { super.%5$s( (%2$s) %3$s().readObject(this, \"%1$s\", super.%4$s())); }%n",
fieldName,
type,
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
inheritanceMetadata.getReaderName(),
inheritanceMetadata.getWriterName() );
}
else {
return String.format(
" if ( %3$s() != null ) { this.%1$s = (%2$s) %3$s().readObject(this, \"%1$s\", this.%1$s); }%n",
fieldName,
type,
EnhancerConstants.INTERCEPTOR_GETTER_NAME );
}
}
@Override
public String buildWriteInterceptionBodyFragment(String fieldName) {
if ( inheritanceMetadata.isInherited() && !inheritanceMetadata.isVisible() ) {
return String.format(
" %2$s localVar = $1;%n" +
" if ( %3$s() != null ) { localVar = (%2$s) %3$s().writeObject(this, \"%1$s\", super.%4$s(), $1); }%n" +
" super.%5$s(localVar);",
fieldName,
type,
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
inheritanceMetadata.getReaderName(),
inheritanceMetadata.getWriterName() );
}
else {
return String.format(
" %2$s localVar = $1;%n" +
" if ( %3$s() != null ) { localVar = (%2$s) %3$s().writeObject(this, \"%1$s\", this.%1$s, $1); }%n" +
" this.%1$s = localVar;",
fieldName,
type,
EnhancerConstants.INTERCEPTOR_GETTER_NAME );
}
}
}
/**
* AttributeTypeDescriptor for primitive types
*/
private static class PrimitiveAttributeTypeDescriptor extends AttributeTypeDescriptor {
private final String type;
private PrimitiveAttributeTypeDescriptor(InheritanceMetadata inheritanceMetadata, Class<?> primitiveType) {
super( inheritanceMetadata );
if ( !primitiveType.isPrimitive() ) {
throw new IllegalArgumentException( "Primitive attribute type descriptor can only be used on primitive types" );
}
// capitalize first letter
this.type = primitiveType.getSimpleName().substring( 0, 1 ).toUpperCase( Locale.ROOT ) + primitiveType.getSimpleName().substring( 1 );
}
@Override
public String buildReadInterceptionBodyFragment(String fieldName) {
if ( inheritanceMetadata.isInherited() && !inheritanceMetadata.isVisible() ) {
return String.format(
" if (%3$s() != null ) { super.%5$s( %3$s().read%2$s(this, \"%1$s\", super.%4$s())); }",
fieldName,
type,
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
inheritanceMetadata.getReaderName(),
inheritanceMetadata.getWriterName() );
}
else {
return String.format(
" if (%3$s() != null ) { this.%1$s = %3$s().read%2$s(this, \"%1$s\", this.%1$s); }",
fieldName,
type,
EnhancerConstants.INTERCEPTOR_GETTER_NAME );
}
}
@Override
public String buildWriteInterceptionBodyFragment(String fieldName) {
if ( inheritanceMetadata.isInherited() && !inheritanceMetadata.isVisible() ) {
return String.format(
" %2$s localVar = $1;%n" +
" if ( %4$s() != null ) { localVar = %4$s().write%3$s(this, \"%1$s\", super.%5$s(), $1); }%n" +
" super.%6$s(localVar);",
fieldName,
type.toLowerCase( Locale.ROOT ),
type,
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
inheritanceMetadata.getReaderName(),
inheritanceMetadata.getWriterName() );
}
else {
return String.format(
" %2$s localVar = $1;%n" +
" if ( %4$s() != null ) { localVar = %4$s().write%3$s(this, \"%1$s\", this.%1$s, $1); }%n" +
" this.%1$s = localVar;",
fieldName,
type.toLowerCase( Locale.ROOT ),
type,
EnhancerConstants.INTERCEPTOR_GETTER_NAME
);
}
}
}
//
private static class InheritanceMetadata {
private boolean inherited;
private boolean visible;
private String readerName;
private String writerName;
public InheritanceMetadata(boolean inherited, boolean visible, String readerName, String writerName) {
this.inherited = inherited;
this.visible = visible;
this.readerName = readerName;
this.writerName = writerName;
}
public boolean isInherited() {
return inherited;
}
public boolean isVisible() {
return visible;
}
public String getReaderName() {
return readerName;
}
public String getWriterName() {
return writerName;
}
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.internal.javassist;
import javassist.CannotCompileException;
import javassist.CtClass;
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.engine.spi.CompositeOwner;
import org.hibernate.engine.spi.CompositeTracker;
import org.hibernate.engine.spi.ManagedComposite;
/**
* enhancer for composite (embeddable) entities
*
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
*/
public class CompositeEnhancer extends PersistentAttributesEnhancer {
public CompositeEnhancer(JavassistEnhancementContext context) {
super( context );
}
public void enhance(CtClass managedCtClass) {
// add the ManagedComposite interface
managedCtClass.addInterface( loadCtClassFromClass( ManagedComposite.class ) );
addInterceptorHandling( managedCtClass );
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
addInLineDirtyHandling( managedCtClass );
}
super.enhance( managedCtClass );
}
/* --- */
private void addInLineDirtyHandling(CtClass managedCtClass) {
managedCtClass.addInterface( loadCtClassFromClass( CompositeTracker.class ) );
final CtClass compositeCtType = loadCtClassFromClass( CompositeOwnerTracker.class );
FieldWriter.addField( managedCtClass, compositeCtType, EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME );
createCompositeTrackerMethod( managedCtClass );
}
private void createCompositeTrackerMethod(CtClass managedCtClass) {
try {
MethodWriter.write( managedCtClass, "" +
"public void %1$s(String name, %3$s tracker) {%n" +
" if (%2$s == null) { %2$s = new %4$s(); }%n" +
" %2$s.add(name, tracker);%n" +
"}",
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME,
CompositeOwner.class.getName(),
CompositeOwnerTracker.class.getName() );
MethodWriter.write( managedCtClass, "" +
"public void %1$s(String name) {%n" +
" if (%2$s != null) { %2$s.removeOwner(name); }%n" +
"}",
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER,
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME );
}
catch (CannotCompileException cce) {
throw new RuntimeException( "createCompositeTrackerMethod failed", cce );
}
}
}

View File

@ -0,0 +1,212 @@
/*
* 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.internal.javassist;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
import javassist.NotFoundException;
import org.hibernate.HibernateException;
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.EnhancerConstants;
import org.hibernate.engine.spi.Managed;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
public class EnhancerImpl implements Enhancer {
private static final CoreMessageLogger log = CoreLogging.messageLogger( Enhancer.class );
protected final JavassistEnhancementContext enhancementContext;
private final ClassPool classPool;
/**
* Constructs the Enhancer, using the given context.
*
* @param enhancementContext Describes the context in which enhancement will occur so as to give access
* to contextual/environmental information.
*/
public EnhancerImpl(EnhancementContext enhancementContext) {
this.enhancementContext = new JavassistEnhancementContext( enhancementContext );
this.classPool = buildClassPool( this.enhancementContext );
}
EnhancerImpl(JavassistEnhancementContext enhancementContext) {
this.enhancementContext = enhancementContext;
this.classPool = buildClassPool( enhancementContext );
}
/**
* Performs the enhancement.
*
* @param className The name of the class whose bytecode is being enhanced.
* @param originalBytes The class's original (pre-enhancement) byte code
*
* @return The enhanced bytecode. Could be the same as the original bytecode if the original was
* already enhanced or we could not enhance it for some reason.
*
* @throws EnhancementException Indicates a problem performing the enhancement
*/
@Override
public synchronized byte[] enhance(String className, byte[] originalBytes) throws EnhancementException {
CtClass managedCtClass = null;
try {
managedCtClass = classPool.makeClassIfNew( new ByteArrayInputStream( originalBytes ) );
return enhance( managedCtClass ) ? getByteCode( managedCtClass ) : null;
}
catch (IOException e) {
log.unableToBuildEnhancementMetamodel( className );
return null;
}
finally {
if ( managedCtClass != null ) {
managedCtClass.detach();
}
}
}
private ClassPool buildClassPool(JavassistEnhancementContext enhancementContext) {
ClassPool classPool = new ClassPool( false ) {
@Override
public ClassLoader getClassLoader() {
return enhancementContext.getLoadingClassLoader();
}
};
ClassLoader loadingClassLoader = enhancementContext.getLoadingClassLoader();
if ( loadingClassLoader != null ) {
classPool.appendClassPath( new LoaderClassPath( loadingClassLoader ) );
}
return classPool;
}
protected CtClass loadCtClassFromClass(Class<?> aClass) {
String resourceName = aClass.getName().replace( '.', '/' ) + ".class";
InputStream resourceAsStream = aClass.getClassLoader().getResourceAsStream( resourceName );
if ( resourceAsStream == null ) {
throw new UncheckedIOException( new FileNotFoundException ( "Not found: " + resourceName ) );
}
try {
return classPool.makeClass( resourceAsStream );
}
catch (IOException e) {
throw new EnhancementException( "Could not prepare Javassist ClassPool", e );
}
finally {
try {
resourceAsStream.close();
}
catch (IOException ioe) {
log.debugf( "An error occurs closing InputStream for class [%s]", aClass.getName() );
}
}
}
private boolean enhance(CtClass managedCtClass) {
// can't effectively enhance interfaces
if ( managedCtClass.isInterface() ) {
log.debugf( "Skipping enhancement of [%s]: it's an interface!", managedCtClass.getName() );
return false;
}
// skip already enhanced classes
if ( alreadyEnhanced( managedCtClass ) ) {
log.debugf( "Skipping enhancement of [%s]: already enhanced", managedCtClass.getName() );
return false;
}
if ( enhancementContext.isEntityClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as Entity", managedCtClass.getName() );
new EntityEnhancer( enhancementContext ).enhance( managedCtClass );
return true;
}
else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as Composite", managedCtClass.getName() );
new CompositeEnhancer( enhancementContext ).enhance( managedCtClass );
return true;
}
else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
log.debugf( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() );
new MappedSuperclassEnhancer( enhancementContext ).enhance( managedCtClass );
return true;
}
else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
log.debugf( "Extended enhancement of [%s]", managedCtClass.getName() );
new PersistentAttributesEnhancer( enhancementContext ).extendedEnhancement( managedCtClass );
return true;
}
else {
log.debugf( "Skipping enhancement of [%s]: not entity or composite", managedCtClass.getName() );
return false;
}
}
// See HHH-10977 HHH-11284 HHH-11404 --- check for declaration of Managed interface on the class, not inherited
private boolean alreadyEnhanced(CtClass managedCtClass) {
try {
for ( CtClass declaredInterface : managedCtClass.getInterfaces() ) {
if ( PersistentAttributesHelper.isAssignable( declaredInterface, Managed.class.getName() ) ) {
return true;
}
}
return false;
}
catch ( NotFoundException e ) {
throw new HibernateException( "Unable to transform class: " + e.getMessage() , e );
}
}
private byte[] getByteCode(CtClass managedCtClass) {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream( byteStream );
try {
managedCtClass.toBytecode( out );
return byteStream.toByteArray();
}
catch (Exception e) {
log.unableToTransformClass( e.getMessage() );
throw new HibernateException( "Unable to transform class: " + e.getMessage() , e );
}
finally {
try {
out.close();
}
catch (IOException ignored) {
}
}
}
protected void addInterceptorHandling(CtClass managedCtClass) {
// interceptor handling is only needed if class has lazy-loadable attributes
if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) {
return;
}
log.debugf( "Weaving in PersistentAttributeInterceptable implementation on [%s]", managedCtClass.getName() );
managedCtClass.addInterface( loadCtClassFromClass( PersistentAttributeInterceptable.class ) );
FieldWriter.addFieldWithGetterAndSetter(
managedCtClass,
loadCtClassFromClass( PersistentAttributeInterceptor.class ),
EnhancerConstants.INTERCEPTOR_FIELD_NAME,
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
EnhancerConstants.INTERCEPTOR_SETTER_NAME
);
}
}

View File

@ -0,0 +1,448 @@
/*
* 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.internal.javassist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;
import javassist.NotFoundException;
import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker;
import org.hibernate.bytecode.enhance.internal.tracker.NoopCollectionTracker;
import org.hibernate.bytecode.enhance.internal.tracker.SimpleCollectionTracker;
import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker;
import org.hibernate.bytecode.enhance.spi.CollectionTracker;
import org.hibernate.bytecode.enhance.spi.EnhancementException;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.SelfDirtinessTracker;
/**
* enhancer for regular entities
*
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
*/
public class EntityEnhancer extends PersistentAttributesEnhancer {
public EntityEnhancer(JavassistEnhancementContext context) {
super( context );
}
// assuming the number of fields is not very high, SimpleFieldTracker implementation it's the fastest
private static final String DIRTY_TRACKER_IMPL = SimpleFieldTracker.class.getName();
private static final String COLLECTION_TRACKER_IMPL = SimpleCollectionTracker.class.getName();
public void enhance(CtClass managedCtClass) {
// add the ManagedEntity interface
managedCtClass.addInterface( loadCtClassFromClass( ManagedEntity.class ) );
addEntityInstanceHandling( managedCtClass );
addEntityEntryHandling( managedCtClass );
addLinkedPreviousHandling( managedCtClass );
addLinkedNextHandling( managedCtClass );
addInterceptorHandling( managedCtClass );
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
addInLineDirtyHandling( managedCtClass );
}
super.enhance( managedCtClass );
}
private void addEntityInstanceHandling(CtClass managedCtClass) {
try {
MethodWriter.write(
managedCtClass,
"public Object %s() { return this; }",
EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME
);
}
catch (CannotCompileException cce) {
throw new EnhancementException(
String.format(
Locale.ROOT,
"Could not enhance entity class [%s] to add EntityEntry getter",
managedCtClass.getName()
),
cce
);
}
}
private void addEntityEntryHandling(CtClass managedCtClass) {
FieldWriter.addFieldWithGetterAndSetter(
managedCtClass, loadCtClassFromClass( EntityEntry.class ),
EnhancerConstants.ENTITY_ENTRY_FIELD_NAME,
EnhancerConstants.ENTITY_ENTRY_GETTER_NAME,
EnhancerConstants.ENTITY_ENTRY_SETTER_NAME
);
}
private void addLinkedPreviousHandling(CtClass managedCtClass) {
FieldWriter.addFieldWithGetterAndSetter(
managedCtClass, loadCtClassFromClass( ManagedEntity.class ),
EnhancerConstants.PREVIOUS_FIELD_NAME,
EnhancerConstants.PREVIOUS_GETTER_NAME,
EnhancerConstants.PREVIOUS_SETTER_NAME
);
}
private void addLinkedNextHandling(CtClass managedCtClass) {
FieldWriter.addFieldWithGetterAndSetter(
managedCtClass, loadCtClassFromClass( ManagedEntity.class ),
EnhancerConstants.NEXT_FIELD_NAME,
EnhancerConstants.NEXT_GETTER_NAME,
EnhancerConstants.NEXT_SETTER_NAME
);
}
private void addInLineDirtyHandling(CtClass managedCtClass) {
managedCtClass.addInterface( loadCtClassFromClass( SelfDirtinessTracker.class ) );
FieldWriter.addField(
managedCtClass,
loadCtClassFromClass( DirtyTracker.class ),
EnhancerConstants.TRACKER_FIELD_NAME
);
if ( collectCollectionFields( managedCtClass ).isEmpty() ) {
createDirtyTrackerMethodsWithoutCollections( managedCtClass );
}
else {
FieldWriter.addField(
managedCtClass,
loadCtClassFromClass( CollectionTracker.class ),
EnhancerConstants.TRACKER_COLLECTION_NAME
);
createDirtyTrackerMethodsWithCollections( managedCtClass );
}
}
private void createDirtyTrackerMethodsWithoutCollections(CtClass managedCtClass) {
try {
MethodWriter.write(
managedCtClass,
"public void %1$s(String name) {%n" +
" if (%2$s == null) { %2$s = new %3$s(); }%n" +
" %2$s.add(name);%n" +
"}",
EnhancerConstants.TRACKER_CHANGER_NAME,
EnhancerConstants.TRACKER_FIELD_NAME,
DIRTY_TRACKER_IMPL
);
MethodWriter.write(
managedCtClass,
"public String[] %1$s() {%n" +
" return (%2$s == null) ? new String[0] : %2$s.get();%n" +
"}",
EnhancerConstants.TRACKER_GET_NAME,
EnhancerConstants.TRACKER_FIELD_NAME
);
MethodWriter.write(
managedCtClass,
"public boolean %1$s() {%n" +
" return (%2$s != null && !%2$s.isEmpty());%n" +
"}",
EnhancerConstants.TRACKER_HAS_CHANGED_NAME,
EnhancerConstants.TRACKER_FIELD_NAME
);
MethodWriter.write(
managedCtClass,
"public void %1$s() {%n" +
" if (%2$s != null) { %2$s.clear(); }%n" +
"}",
EnhancerConstants.TRACKER_CLEAR_NAME,
EnhancerConstants.TRACKER_FIELD_NAME
);
MethodWriter.write(
managedCtClass,
"public void %1$s(boolean f) {%n" +
" if (%2$s == null) %2$s = new %3$s();%n %2$s.suspend(f);%n" +
"}",
EnhancerConstants.TRACKER_SUSPEND_NAME,
EnhancerConstants.TRACKER_FIELD_NAME ,
DIRTY_TRACKER_IMPL
);
MethodWriter.write(
managedCtClass,
"public %s %s() { return %s.INSTANCE; }",
CollectionTracker.class.getName(),
EnhancerConstants.TRACKER_COLLECTION_GET_NAME,
NoopCollectionTracker.class.getName()
);
}
catch (CannotCompileException cce) {
throw new RuntimeException( "createDirtyTrackerMethodsWithoutCollections failed", cce );
}
}
private void createDirtyTrackerMethodsWithCollections(CtClass managedCtClass) {
try {
MethodWriter.write(
managedCtClass,
"public void %1$s(String name) {%n" +
" if (%2$s == null) { %2$s = new %3$s(); }%n" +
" %2$s.add(name);%n" +
"}",
EnhancerConstants.TRACKER_CHANGER_NAME,
EnhancerConstants.TRACKER_FIELD_NAME,
DIRTY_TRACKER_IMPL
);
createCollectionDirtyCheckMethod( managedCtClass );
createCollectionDirtyCheckGetFieldsMethod( managedCtClass );
createClearDirtyCollectionMethod( managedCtClass );
MethodWriter.write(
managedCtClass,
"public String[] %1$s() {%n" +
" if(%3$s == null) {%n" +
" return (%2$s == null) ? new String[0] : %2$s.get();%n" +
" } else {%n" +
" if (%2$s == null) %2$s = new %5$s();%n" +
" %4$s(%2$s);%n" +
" return %2$s.get();%n" +
" }%n" +
"}",
EnhancerConstants.TRACKER_GET_NAME,
EnhancerConstants.TRACKER_FIELD_NAME,
EnhancerConstants.TRACKER_COLLECTION_NAME,
EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME,
DIRTY_TRACKER_IMPL
);
MethodWriter.write(
managedCtClass,
"public boolean %1$s() {%n" +
" return (%2$s != null && !%2$s.isEmpty()) || %3$s();%n" +
"}",
EnhancerConstants.TRACKER_HAS_CHANGED_NAME,
EnhancerConstants.TRACKER_FIELD_NAME,
EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME
);
MethodWriter.write(
managedCtClass,
"public void %1$s() {%n" +
" if (%2$s != null) { %2$s.clear(); }%n" +
" %3$s();%n" +
"}",
EnhancerConstants.TRACKER_CLEAR_NAME,
EnhancerConstants.TRACKER_FIELD_NAME,
EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME
);
MethodWriter.write(
managedCtClass,
"public void %1$s(boolean f) {%n" +
" if (%2$s == null) %2$s = new %3$s();%n %2$s.suspend(f);%n" +
"}",
EnhancerConstants.TRACKER_SUSPEND_NAME,
EnhancerConstants.TRACKER_FIELD_NAME ,
DIRTY_TRACKER_IMPL
);
MethodWriter.write(
managedCtClass,
"public %s %s() { return %s; }",
CollectionTracker.class.getName(),
EnhancerConstants.TRACKER_COLLECTION_GET_NAME,
EnhancerConstants.TRACKER_COLLECTION_NAME
);
}
catch (CannotCompileException cce) {
throw new RuntimeException( "createDirtyTrackerMethodsWithCollections failed", cce );
}
}
private List<CtField> collectCollectionFields(CtClass managedCtClass) {
List<CtField> collectionList = new ArrayList<>();
for ( CtField ctField : managedCtClass.getDeclaredFields() ) {
// skip static fields and skip fields added by enhancement
if ( Modifier.isStatic( ctField.getModifiers() ) || ctField.getName().startsWith( "$$_hibernate_" ) ) {
continue;
}
if ( enhancementContext.isPersistentField( ctField ) && !enhancementContext.isMappedCollection( ctField ) ) {
if ( PersistentAttributesHelper.isAssignable( ctField, Collection.class.getName() ) ||
PersistentAttributesHelper.isAssignable( ctField, Map.class.getName() ) ) {
collectionList.add( ctField );
}
}
}
// HHH-10646 Add fields inherited from @MappedSuperclass
// HHH-10981 There is no need to do it for @MappedSuperclass
if ( !enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
collectionList.addAll( collectInheritCollectionFields( managedCtClass ) );
}
return collectionList;
}
private Collection<CtField> collectInheritCollectionFields(CtClass managedCtClass) {
if ( managedCtClass == null || Object.class.getName().equals( managedCtClass.getName() ) ) {
return Collections.emptyList();
}
try {
CtClass managedCtSuperclass = managedCtClass.getSuperclass();
if ( !enhancementContext.isMappedSuperclassClass( managedCtSuperclass ) ) {
return collectInheritCollectionFields( managedCtSuperclass );
}
List<CtField> collectionList = new ArrayList<>();
for ( CtField ctField : managedCtSuperclass.getDeclaredFields() ) {
if ( !Modifier.isStatic( ctField.getModifiers() ) ) {
if ( enhancementContext.isPersistentField( ctField ) && !enhancementContext.isMappedCollection( ctField ) ) {
if ( PersistentAttributesHelper.isAssignable( ctField, Collection.class.getName() ) ||
PersistentAttributesHelper.isAssignable( ctField, Map.class.getName() ) ) {
collectionList.add( ctField );
}
}
}
}
collectionList.addAll( collectInheritCollectionFields( managedCtSuperclass ) );
return collectionList;
}
catch ( NotFoundException nfe ) {
return Collections.emptyList();
}
}
private void createCollectionDirtyCheckMethod(CtClass managedCtClass) {
try {
final StringBuilder body = new StringBuilder();
body.append(
String.format(
"private boolean %1$s() {%n" +
" if (%2$s == null) { return false; }%n%n",
EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME,
EnhancerConstants.TRACKER_COLLECTION_NAME
)
);
for ( CtField ctField : collectCollectionFields( managedCtClass ) ) {
body.append(
String.format(
" // collection field [%1$s]%n" +
" if (%1$s == null && %2$s.getSize(\"%1$s\") != -1) { return true; }%n" +
" if (%1$s != null && %2$s.getSize(\"%1$s\") != %1$s.size()) { return true; }%n%n",
ctField.getName(),
EnhancerConstants.TRACKER_COLLECTION_NAME
)
);
}
body.append( " return false;%n}" );
MethodWriter.write( managedCtClass, body.toString() );
}
catch (CannotCompileException cce) {
throw new RuntimeException( "createCollectionDirtyCheckMethod failed", cce );
}
}
private void createCollectionDirtyCheckGetFieldsMethod(CtClass managedCtClass) {
try {
final StringBuilder body = new StringBuilder();
body.append(
String.format(
"private void %1$s(%3$s tracker) {%n" +
" if (%2$s == null) { return; }%n%n",
EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME,
EnhancerConstants.TRACKER_COLLECTION_NAME,
DirtyTracker.class.getName()
)
);
for ( CtField ctField : collectCollectionFields( managedCtClass ) ) {
body.append(
String.format(
" // Collection field [%1$s]%n" +
" if (%1$s == null && %2$s.getSize(\"%1$s\") != -1) { tracker.add(\"%1$s\"); }%n" +
" if (%1$s != null && %2$s.getSize(\"%1$s\") != %1$s.size()) { tracker.add(\"%1$s\"); }%n%n",
ctField.getName(),
EnhancerConstants.TRACKER_COLLECTION_NAME
)
);
}
body.append( "}" );
MethodWriter.write( managedCtClass, body.toString() );
}
catch (CannotCompileException cce) {
throw new RuntimeException( "createCollectionDirtyCheckGetFieldsMethod failed", cce );
}
}
private void createClearDirtyCollectionMethod(CtClass managedCtClass) throws CannotCompileException {
try {
final StringBuilder body = new StringBuilder();
body.append(
String.format(
"private void %1$s() {%n" +
" if (%2$s == null) { %2$s = new %3$s(); }%n" +
" %4$s lazyInterceptor = null;%n",
EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME,
EnhancerConstants.TRACKER_COLLECTION_NAME,
COLLECTION_TRACKER_IMPL,
LazyAttributeLoadingInterceptor.class.getName()
)
);
if ( PersistentAttributesHelper.isAssignable( managedCtClass, PersistentAttributeInterceptable.class.getName() ) ) {
body.append(
String.format(
" if(%1$s != null && %1$s instanceof %2$s) lazyInterceptor = (%2$s) %1$s;%n%n",
EnhancerConstants.INTERCEPTOR_FIELD_NAME,
LazyAttributeLoadingInterceptor.class.getName()
)
);
}
for ( CtField ctField : collectCollectionFields( managedCtClass ) ) {
body.append(
String.format(
" // collection field [%1$s]%n" +
" if (lazyInterceptor == null || lazyInterceptor.isAttributeLoaded(\"%1$s\")) {%n" +
" if (%1$s == null) { %2$s.add(\"%1$s\", -1); }%n" +
" else { %2$s.add(\"%1$s\", %1$s.size()); }%n" +
" }%n%n",
ctField.getName(),
EnhancerConstants.TRACKER_COLLECTION_NAME
)
);
}
body.append( "}" );
MethodWriter.write( managedCtClass, body.toString() );
}
catch (CannotCompileException cce) {
throw cce;
}
}
}

View File

@ -0,0 +1,83 @@
/*
* 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.internal.javassist;
import javax.persistence.Transient;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.annotation.Annotation;
import org.hibernate.bytecode.enhance.spi.EnhancementException;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
/**
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
*/
public class FieldWriter {
private static final CoreMessageLogger log = CoreLogging.messageLogger( FieldWriter.class );
private FieldWriter() { }
/* --- */
/**
* Add enhancement field
*/
public static void addField(CtClass target, CtClass type, String field) {
addPrivateTransient( target, type, field );
}
/**
* Add enhancement field with getter and setter
*/
public static void addFieldWithGetterAndSetter(CtClass target, CtClass type, String field, String getter, String setter) {
addPrivateTransient( target, type, field );
MethodWriter.addGetter( target, field, getter );
MethodWriter.addSetter( target, field, setter );
}
/* --- */
private static void addPrivateTransient(CtClass target, CtClass type, String name) {
addWithModifiers( target, type, name, Modifier.PRIVATE | Modifier.TRANSIENT, Transient.class );
log.debugf( "Wrote field into [%s]: @Transient private transient %s %s;", target.getName(), type.getName(), name );
}
private static void addWithModifiers(CtClass target, CtClass type, String name, int modifiers, Class<?> ... annotations ) {
try {
final CtField f = new CtField( type, name, target );
f.setModifiers( f.getModifiers() | modifiers );
addAnnotations( f.getFieldInfo(), annotations );
target.addField( f );
}
catch (CannotCompileException cce) {
final String msg = String.format( "Could not enhance class [%s] to add field [%s]", target.getName(), name );
throw new EnhancementException( msg, cce );
}
}
/* --- */
private static void addAnnotations(FieldInfo fieldInfo, Class<?>[] annotations) {
AnnotationsAttribute annotationsAttribute = (AnnotationsAttribute) fieldInfo.getAttribute( AnnotationsAttribute.visibleTag );
if ( annotationsAttribute == null ) {
annotationsAttribute = new AnnotationsAttribute( fieldInfo.getConstPool(), AnnotationsAttribute.visibleTag );
fieldInfo.addAttribute( annotationsAttribute );
}
for (Class<?> annotation : annotations) {
annotationsAttribute.addAnnotation( new Annotation( annotation.getName(), fieldInfo.getConstPool() ) );
}
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.internal.javassist;
import javassist.CtClass;
import javassist.CtField;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.UnloadedField;
public class JavassistEnhancementContext {
private final EnhancementContext enhancementContext;
public JavassistEnhancementContext(EnhancementContext enhancementContext) {
this.enhancementContext = enhancementContext;
}
public ClassLoader getLoadingClassLoader() {
return enhancementContext.getLoadingClassLoader();
}
public boolean isEntityClass(CtClass classDescriptor) {
return enhancementContext.isEntityClass( new UnloadedCtClass( classDescriptor ) );
}
public boolean isCompositeClass(CtClass classDescriptor) {
return enhancementContext.isCompositeClass( new UnloadedCtClass( classDescriptor ) );
}
public boolean isMappedSuperclassClass(CtClass classDescriptor) {
return enhancementContext.isMappedSuperclassClass( new UnloadedCtClass( classDescriptor ) );
}
public boolean doBiDirectionalAssociationManagement(CtField field) {
return enhancementContext.doBiDirectionalAssociationManagement( new UnloadedCtField( field ) );
}
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
return enhancementContext.doDirtyCheckingInline( new UnloadedCtClass( classDescriptor ) );
}
public boolean doExtendedEnhancement(CtClass classDescriptor) {
return enhancementContext.doExtendedEnhancement( new UnloadedCtClass( classDescriptor ) );
}
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
return enhancementContext.hasLazyLoadableAttributes( new UnloadedCtClass( classDescriptor ) );
}
public boolean isPersistentField(CtField ctField) {
return enhancementContext.isPersistentField( new UnloadedCtField( ctField ) );
}
public CtField[] order(CtField[] persistentFields) {
UnloadedField[] unloadedFields = new UnloadedField[persistentFields.length];
for ( int i = 0; i < unloadedFields.length; i++ ) {
unloadedFields[i] = new UnloadedCtField( persistentFields[i] );
}
UnloadedField[] ordered = enhancementContext.order( unloadedFields );
CtField[] orderedFields = new CtField[persistentFields.length];
for ( int i = 0; i < orderedFields.length; i++ ) {
orderedFields[i] = ( (UnloadedCtField) ordered[i] ).ctField;
}
return orderedFields;
}
public boolean isLazyLoadable(CtField field) {
return enhancementContext.isLazyLoadable( new UnloadedCtField( field ) );
}
public boolean isMappedCollection(CtField field) {
return enhancementContext.isMappedCollection( new UnloadedCtField( field ) );
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.internal.javassist;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.engine.spi.ManagedMappedSuperclass;
/**
* enhancer for mapped superclass
*
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
*/
public class MappedSuperclassEnhancer extends PersistentAttributesEnhancer {
public MappedSuperclassEnhancer(JavassistEnhancementContext context) {
super( context );
}
public void enhance(CtClass managedCtClass) {
// Add the Managed interface
managedCtClass.addInterface( loadCtClassFromClass( ManagedMappedSuperclass.class ) );
super.enhance( managedCtClass );
}
// Generate 'template' methods for each attribute. This will be overriden by the actual entities
@Override
protected CtMethod generateFieldReader(
CtClass managedCtClass,
CtField persistentField,
AttributeTypeDescriptor typeDescriptor) {
String fieldName = persistentField.getName();
String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName;
return MethodWriter.addGetter( managedCtClass, fieldName, readerName );
}
@Override
protected CtMethod generateFieldWriter(
CtClass managedCtClass,
CtField persistentField,
AttributeTypeDescriptor typeDescriptor) {
String fieldName = persistentField.getName();
String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName;
return MethodWriter.addSetter( managedCtClass, fieldName, writerName );
}
}

View File

@ -0,0 +1,109 @@
/*
* 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.internal.javassist;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;
import javassist.bytecode.ConstPool;
import org.hibernate.bytecode.enhance.spi.EnhancementException;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
/**
* utility class to compile methods and add the to class files
*
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
*/
public class MethodWriter {
private static final CoreMessageLogger log = CoreLogging.messageLogger( MethodWriter.class );
private MethodWriter() { }
/* --- */
/**
* convenience method that builds a method from a format string. {@see String.format} for more details
*
* @throws CannotCompileException
*/
public static CtMethod write(CtClass target, String format, Object ... args) throws CannotCompileException {
String body = String.format( format, args );
// System.out.printf( "writing method into [%s]:%n%s%n", target.getName(), body );
log.debugf( "writing method into [%s]:%n%s", target.getName(), body );
CtMethod method = CtNewMethod.make( body, target );
target.addMethod( method );
return method;
}
/* --- */
public static CtMethod addGetter(CtClass target, String field, String name) {
CtField actualField = null;
try {
actualField = target.getField( field );
log.debugf( "Writing getter method [%s] into [%s] for field [%s]", name, target.getName(), field );
CtMethod method = CtNewMethod.getter( name, target.getField( field ) );
target.addMethod( method );
return method;
}
catch (CannotCompileException cce) {
try {
// Fall back to create a getter from delegation.
CtMethod method = CtNewMethod.delegator( CtNewMethod.getter( name, actualField ), target );
target.addMethod( method );
return method;
}
catch (CannotCompileException ignored) {
String msg = String.format( "Could not enhance class [%s] to add method [%s] for field [%s]", target.getName(), name, field );
throw new EnhancementException( msg, cce );
}
}
catch (NotFoundException nfe) {
String msg = String.format( "Could not enhance class [%s] to add method [%s] for field [%s]", target.getName(), name, field );
throw new EnhancementException( msg, nfe );
}
}
public static CtMethod addSetter(CtClass target, String field, String name) {
CtField actualField = null;
try {
actualField = target.getField( field );
log.debugf( "Writing setter method [%s] into [%s] for field [%s]", name, target.getName(), field );
CtMethod method = CtNewMethod.setter( name, actualField );
target.addMethod( method );
return method;
}
catch (CannotCompileException cce) {
try {
// Fall back to create a getter from delegation.
CtMethod method = CtNewMethod.delegator( CtNewMethod.setter( name, actualField ), target );
target.addMethod( method );
return method;
}
catch (CannotCompileException ignored) {
String msg = String.format( "Could not enhance class [%s] to add method [%s] for field [%s]", target.getName(), name, field );
throw new EnhancementException( msg, cce );
}
}
catch (NotFoundException nfe) {
String msg = String.format( "Could not enhance class [%s] to add method [%s] for field [%s]", target.getName(), name, field );
throw new EnhancementException( msg, nfe );
}
}
/* --- */
public static int addMethod(ConstPool cPool, CtMethod method) {
return cPool.addMethodrefInfo( cPool.getThisClassInfo(), method.getName(), method.getSignature() );
}
}

View File

@ -0,0 +1,751 @@
/*
* 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.internal.javassist;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.Embedded;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.ConstPool;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.bytecode.stackmap.MapMaker;
import org.hibernate.Hibernate;
import org.hibernate.bytecode.enhance.spi.EnhancementException;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.engine.spi.CompositeOwner;
import org.hibernate.engine.spi.CompositeTracker;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
/**
* enhancer for persistent attributes of any type of entity
*
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
*/
public class PersistentAttributesEnhancer extends EnhancerImpl {
private static final CoreMessageLogger log = CoreLogging.messageLogger( PersistentAttributesEnhancer.class );
public PersistentAttributesEnhancer(JavassistEnhancementContext context) {
super( context );
}
public void enhance(CtClass managedCtClass) {
final IdentityHashMap<String, PersistentAttributeAccessMethods> attrDescriptorMap = new IdentityHashMap<>();
for ( CtField persistentField : collectPersistentFields( managedCtClass ) ) {
attrDescriptorMap.put(
persistentField.getName(), enhancePersistentAttribute(
managedCtClass,
persistentField
)
);
}
// find all references to the transformed fields and replace with calls to the added reader/writer methods
enhanceAttributesAccess( managedCtClass, attrDescriptorMap );
// same thing for direct access to fields of other entities
if ( this.enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
extendedEnhancement( managedCtClass );
}
}
private CtField[] collectPersistentFields(CtClass managedCtClass) {
List<CtField> persistentFieldList = new ArrayList<>();
for ( CtField ctField : managedCtClass.getDeclaredFields() ) {
// skip static fields and skip fields added by enhancement and outer reference in inner classes
if ( ctField.getName().startsWith( "$$_hibernate_" ) || "this$0".equals( ctField.getName() ) ) {
continue;
}
if ( !Modifier.isStatic( ctField.getModifiers() ) && enhancementContext.isPersistentField( ctField ) ) {
persistentFieldList.add( ctField );
}
}
// HHH-10646 Add fields inherited from @MappedSuperclass
// HHH-10981 There is no need to do it for @MappedSuperclass
if ( !enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
persistentFieldList.addAll( collectInheritPersistentFields( managedCtClass ) );
}
CtField[] orderedFields = enhancementContext.order( persistentFieldList.toArray( new CtField[0] ) );
if ( log.isDebugEnabled() ) {
log.debugf(
"Persistent fields for entity %s: %s",
managedCtClass.getName(),
Arrays.toString( orderedFields )
);
}
return orderedFields;
}
private Collection<CtField> collectInheritPersistentFields(CtClass managedCtClass) {
if ( managedCtClass == null || Object.class.getName().equals( managedCtClass.getName() ) ) {
return Collections.emptyList();
}
try {
CtClass managedCtSuperclass = managedCtClass.getSuperclass();
if ( enhancementContext.isEntityClass( managedCtSuperclass ) ) {
return Collections.emptyList();
}
else if ( !enhancementContext.isMappedSuperclassClass( managedCtSuperclass ) ) {
return collectInheritPersistentFields( managedCtSuperclass );
}
log.debugf( "Found @MappedSuperclass %s to collectPersistenceFields", managedCtSuperclass.getName() );
List<CtField> persistentFieldList = new ArrayList<>();
for ( CtField ctField : managedCtSuperclass.getDeclaredFields() ) {
if ( ctField.getName().startsWith( "$$_hibernate_" ) || "this$0".equals( ctField.getName() ) ) {
continue;
}
if ( !Modifier.isStatic( ctField.getModifiers() ) && enhancementContext.isPersistentField( ctField ) ) {
persistentFieldList.add( ctField );
}
}
persistentFieldList.addAll( collectInheritPersistentFields( managedCtSuperclass ) );
return persistentFieldList;
}
catch ( NotFoundException nfe ) {
log.warnf( "Could not find the superclass of %s", managedCtClass );
return Collections.emptyList();
}
}
private PersistentAttributeAccessMethods enhancePersistentAttribute(CtClass managedCtClass, CtField persistentField) {
try {
AttributeTypeDescriptor typeDescriptor = AttributeTypeDescriptor.resolve( managedCtClass, persistentField );
return new PersistentAttributeAccessMethods(
generateFieldReader( managedCtClass, persistentField, typeDescriptor ),
generateFieldWriter( managedCtClass, persistentField, typeDescriptor )
);
}
catch (Exception e) {
final String msg = String.format(
"Unable to enhance persistent attribute [%s:%s]",
managedCtClass.getName(),
persistentField.getName()
);
throw new EnhancementException( msg, e );
}
}
protected CtMethod generateFieldReader(
CtClass managedCtClass,
CtField persistentField,
AttributeTypeDescriptor typeDescriptor) {
String fieldName = persistentField.getName();
String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName;
String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName;
CtMethod tmpSuperReader = null;
CtMethod tmpSuperWriter = null;
CtMethod reader = null;
try {
boolean declared = persistentField.getDeclaringClass().equals( managedCtClass );
String declaredReadFragment = "this." + fieldName;
String superReadFragment = "super." + readerName + "()";
if ( !declared ) {
// create a temporary getter on the supper entity to be able to compile our code
try {
persistentField.getDeclaringClass().getDeclaredMethod( readerName );
persistentField.getDeclaringClass().getDeclaredMethod( writerName );
}
catch ( NotFoundException nfe ) {
tmpSuperReader = MethodWriter.addGetter( persistentField.getDeclaringClass(), persistentField.getName(), readerName );
tmpSuperWriter = MethodWriter.addSetter( persistentField.getDeclaringClass(), persistentField.getName(), writerName );
}
}
// read attempts only have to deal lazy-loading support, not dirty checking;
// so if the field is not enabled as lazy-loadable return a plain simple getter as the reader
if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) || !enhancementContext.isLazyLoadable( persistentField ) ) {
reader = MethodWriter.write(
managedCtClass, "public %s %s() { return %s;%n}",
persistentField.getType().getName(),
readerName,
declared ? declaredReadFragment : superReadFragment
);
}
else {
reader = MethodWriter.write(
managedCtClass, "public %s %s() {%n%s%n return %s;%n}",
persistentField.getType().getName(),
readerName,
typeDescriptor.buildReadInterceptionBodyFragment( fieldName ),
declared ? declaredReadFragment : superReadFragment
);
}
if ( tmpSuperReader != null ) {
persistentField.getDeclaringClass().removeMethod( tmpSuperReader );
}
if ( tmpSuperWriter != null ) {
persistentField.getDeclaringClass().removeMethod( tmpSuperWriter );
}
return reader;
}
catch (CannotCompileException cce) {
final String msg = String.format(
"Could not enhance entity class [%s] to add field reader method [%s]",
managedCtClass.getName(),
readerName
);
throw new EnhancementException( msg, cce );
}
catch (NotFoundException nfe) {
final String msg = String.format(
"Could not enhance entity class [%s] to add field reader method [%s]",
managedCtClass.getName(),
readerName
);
throw new EnhancementException( msg, nfe );
}
}
protected CtMethod generateFieldWriter(
CtClass managedCtClass,
CtField persistentField,
AttributeTypeDescriptor typeDescriptor) {
String fieldName = persistentField.getName();
String readerName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName;
String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName;
CtMethod tmpSuperReader = null;
CtMethod tmpSuperWriter = null;
CtMethod writer;
try {
boolean declared = persistentField.getDeclaringClass().equals( managedCtClass );
String declaredWriteFragment = "this." + fieldName + "=" + fieldName + ";";
String superWriteFragment = "super." + writerName + "(" + fieldName + ");";
if ( !declared ) {
// create a temporary setter on the supper entity to be able to compile our code
try {
persistentField.getDeclaringClass().getDeclaredMethod( readerName );
persistentField.getDeclaringClass().getDeclaredMethod( writerName );
}
catch ( NotFoundException nfe ) {
tmpSuperReader = MethodWriter.addGetter( persistentField.getDeclaringClass(), persistentField.getName(), readerName );
tmpSuperWriter = MethodWriter.addSetter( persistentField.getDeclaringClass(), persistentField.getName(), writerName );
}
}
if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) || !enhancementContext.isLazyLoadable( persistentField ) ) {
writer = MethodWriter.write(
managedCtClass,
"public void %s(%s %s) {%n %s%n}",
writerName,
persistentField.getType().getName(),
fieldName,
declared ? declaredWriteFragment : superWriteFragment
);
}
else {
writer = MethodWriter.write(
managedCtClass,
"public void %s(%s %s) {%n%s%n}",
writerName,
persistentField.getType().getName(),
fieldName,
typeDescriptor.buildWriteInterceptionBodyFragment( fieldName )
);
}
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
writer.insertBefore(
String.format(
" if (%1$s != null) { %1$s.callOwner(\"\"); }%n",
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME
)
);
}
else {
writer.insertBefore( typeDescriptor.buildInLineDirtyCheckingBodyFragment( enhancementContext, persistentField ) );
}
handleCompositeField( managedCtClass, persistentField, writer );
}
if ( enhancementContext.doBiDirectionalAssociationManagement( persistentField ) ) {
handleBiDirectionalAssociation( managedCtClass, persistentField, writer );
}
if ( tmpSuperReader != null ) {
persistentField.getDeclaringClass().removeMethod( tmpSuperReader );
}
if ( tmpSuperWriter != null ) {
persistentField.getDeclaringClass().removeMethod( tmpSuperWriter );
}
return writer;
}
catch (CannotCompileException cce) {
final String msg = String.format(
"Could not enhance entity class [%s] to add field writer method [%s]",
managedCtClass.getName(),
writerName
);
throw new EnhancementException( msg, cce );
}
catch (NotFoundException nfe) {
final String msg = String.format(
"Could not enhance entity class [%s] to add field writer method [%s]",
managedCtClass.getName(),
writerName
);
throw new EnhancementException( msg, nfe );
}
}
private void handleBiDirectionalAssociation(CtClass managedCtClass, CtField persistentField, CtMethod fieldWriter)
throws NotFoundException, CannotCompileException {
if ( !PersistentAttributesHelper.isPossibleBiDirectionalAssociation( persistentField ) ) {
return;
}
final CtClass targetEntity = PersistentAttributesHelper.getTargetEntityClass( managedCtClass, persistentField );
if ( targetEntity == null ) {
log.infof(
"Bi-directional association not managed for field [%s#%s]: Could not find target type",
managedCtClass.getName(),
persistentField.getName()
);
return;
}
final String mappedBy = PersistentAttributesHelper.getMappedBy( persistentField, targetEntity, enhancementContext );
if ( mappedBy == null || mappedBy.isEmpty() ) {
log.infof(
"Bi-directional association not managed for field [%s#%s]: Could not find target field in [%s]",
managedCtClass.getName(),
persistentField.getName(),
targetEntity.getName()
);
return;
}
// create a temporary getter and setter on the target entity to be able to compile our code
final String mappedByGetterName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + mappedBy;
final String mappedBySetterName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + mappedBy;
CtMethod getter;
CtMethod setter;
boolean tmpTargetMethods = false;
try {
getter = targetEntity.getDeclaredMethod( mappedByGetterName );
setter = targetEntity.getDeclaredMethod( mappedByGetterName );
}
catch ( NotFoundException nfe ) {
getter = MethodWriter.addGetter( targetEntity, mappedBy, mappedByGetterName );
setter = MethodWriter.addSetter( targetEntity, mappedBy, mappedBySetterName );
tmpTargetMethods = true;
}
// code fragments to check loaded state. We don't want to trigger lazy loading in association management code
String currentAssociationLoaded = String.format(
"%s.isPropertyInitialized(this.%s, \"%s\")",
Hibernate.class.getName(),
persistentField.getName(),
mappedBy
);
String targetElementLoaded = String.format(
"%s.isPropertyInitialized(target, \"%s\")",
Hibernate.class.getName(),
mappedBy
);
String newAssociationLoaded = String.format(
"%s.isPropertyInitialized($1, \"%s\")",
Hibernate.class.getName(),
mappedBy
);
if ( PersistentAttributesHelper.hasAnnotation( persistentField, OneToOne.class ) ) {
// only unset when $1 != null to avoid recursion
fieldWriter.insertBefore(
String.format(
" if (this.%1$s != null && %2$s && $1 != null) { this.%1$s.%3$s(null); }%n",
persistentField.getName(),
currentAssociationLoaded,
mappedBySetterName
)
);
fieldWriter.insertAfter(
String.format(
" if ($1 != null && %s && $1.%s() != this) { $1.%s(this); }%n",
newAssociationLoaded,
mappedByGetterName,
mappedBySetterName
)
);
}
if ( PersistentAttributesHelper.hasAnnotation( persistentField, OneToMany.class ) ) {
boolean isMap = PersistentAttributesHelper.isAssignable( persistentField.getType(), Map.class.getName() );
String toArrayMethod = isMap ? "values().toArray()" : "toArray()";
// only remove elements not in the new collection or else we would loose those elements
// don't use iterator to avoid ConcurrentModException
fieldWriter.insertBefore(
String.format(
" if (this.%3$s != null && %1$s) {%n" +
" Object[] array = this.%3$s.%2$s;%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %4$s target = (%4$s) array[i];%n" +
" if ($1 == null || !$1.contains(target)) { target.%5$s(null); }%n" +
" }%n" +
" }%n",
currentAssociationLoaded,
toArrayMethod,
persistentField.getName(),
targetEntity.getName(),
mappedBySetterName
)
);
fieldWriter.insertAfter(
String.format(
" if ($1 != null && %1$s) {%n" +
" Object[] array = $1.%2$s;%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %4$s target = (%4$s) array[i];%n" +
" if (%3$s && target.%5$s() != this) { target.%6$s(this); }%n" +
" }%n" +
" }%n",
newAssociationLoaded,
toArrayMethod,
targetElementLoaded,
targetEntity.getName(),
mappedByGetterName,
mappedBySetterName
)
);
}
if ( PersistentAttributesHelper.hasAnnotation( persistentField, ManyToOne.class ) ) {
fieldWriter.insertBefore(
String.format(
" if (this.%2$s != null && %1$s && this.%2$s.%3$s() != null) { this.%2$s.%3$s().remove(this); }%n",
currentAssociationLoaded,
persistentField.getName(),
mappedByGetterName
)
);
// check .contains(this) to avoid double inserts (but preventing duplicates)
fieldWriter.insertAfter(
String.format(
" if ($1 != null && %s) {%n" +
" java.util.Collection c = $1.%s();%n" +
" if (c != null && !c.contains(this)) { c.add(this); }%n" +
" }%n",
newAssociationLoaded,
mappedByGetterName
)
);
}
if ( PersistentAttributesHelper.hasAnnotation( persistentField, ManyToMany.class ) ) {
if ( PersistentAttributesHelper.isAssignable( persistentField.getType(), Map.class.getName() ) ||
PersistentAttributesHelper.isAssignable( targetEntity.getField( mappedBy ).getType(), Map.class.getName() ) ) {
log.infof(
"Bi-directional association not managed for field [%s#%s]: @ManyToMany in java.util.Map attribute not supported ",
managedCtClass.getName(),
persistentField.getName()
);
return;
}
fieldWriter.insertBefore(
String.format(
" if (this.%2$s != null && %1$s) {%n" +
" Object[] array = this.%2$s.toArray();%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %3$s target = (%3$s) array[i];%n" +
" if ($1 == null || !$1.contains(target)) { target.%4$s().remove(this); }%n" +
" }%n" +
" }%n",
currentAssociationLoaded,
persistentField.getName(),
targetEntity.getName(),
mappedByGetterName
)
);
fieldWriter.insertAfter(
String.format(
" if ($1 != null && %s) {%n" +
" Object[] array = $1.toArray();%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %s target = (%s) array[i];%n" +
" if (%s) {%n" +
" java.util.Collection c = target.%s();%n" +
" if (c != this && c != null) { c.add(this); }%n" +
" }%n" +
" }%n" +
" }%n",
newAssociationLoaded,
targetEntity.getName(),
targetEntity.getName(),
targetElementLoaded,
mappedByGetterName
)
);
}
// implementation note: association management @OneToMany and @ManyToMay works for add() operations but for remove() a snapshot of the collection is needed so we know what associations to break.
// another approach that could force that behavior would be to return Collections.unmodifiableCollection() ...
if ( tmpTargetMethods ) {
targetEntity.removeMethod( getter );
targetEntity.removeMethod( setter );
}
}
private void handleCompositeField(CtClass managedCtClass, CtField persistentField, CtMethod fieldWriter)
throws NotFoundException, CannotCompileException {
if ( !enhancementContext.isCompositeClass( persistentField.getType() ) ||
!PersistentAttributesHelper.hasAnnotation( persistentField, Embedded.class ) ) {
return;
}
// make sure to add the CompositeOwner interface
addCompositeOwnerInterface( managedCtClass );
String readFragment = persistentField.visibleFrom( managedCtClass ) ? persistentField.getName() : "super." + EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + persistentField.getName() + "()";
// cleanup previous owner
fieldWriter.insertBefore(
String.format(
"if (%1$s != null) { ((%2$s) %1$s).%3$s(\"%4$s\"); }%n",
readFragment,
CompositeTracker.class.getName(),
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER,
persistentField.getName()
)
);
// trigger track changes
fieldWriter.insertAfter(
String.format(
"if (%1$s != null) { ((%2$s) %1$s).%4$s(\"%6$s\", (%3$s) this); }%n" +
"%5$s(\"%6$s\");",
readFragment,
CompositeTracker.class.getName(),
CompositeOwner.class.getName(),
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
EnhancerConstants.TRACKER_CHANGER_NAME,
persistentField.getName()
)
);
}
private void addCompositeOwnerInterface(CtClass managedCtClass) throws NotFoundException, CannotCompileException {
CtClass compositeOwnerCtClass = managedCtClass.getClassPool().get( CompositeOwner.class.getName() );
// HHH-10540 only add the interface once
for ( CtClass i : managedCtClass.getInterfaces() ) {
if ( i.subclassOf( compositeOwnerCtClass ) ) {
return;
}
}
managedCtClass.addInterface( compositeOwnerCtClass );
if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
// if a composite have a embedded field we need to implement the TRACKER_CHANGER_NAME method as well
MethodWriter.write(
managedCtClass,
"public void %1$s(String name) {%n" +
" if (%2$s != null) { %2$s.callOwner(\".\" + name); }%n}",
EnhancerConstants.TRACKER_CHANGER_NAME,
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME
);
}
}
protected void enhanceAttributesAccess(
CtClass managedCtClass,
IdentityHashMap<String, PersistentAttributeAccessMethods> attributeDescriptorMap) {
final ConstPool constPool = managedCtClass.getClassFile().getConstPool();
final ClassPool classPool = managedCtClass.getClassPool();
for ( Object oMethod : managedCtClass.getClassFile().getMethods() ) {
final MethodInfo methodInfo = (MethodInfo) oMethod;
final String methodName = methodInfo.getName();
// skip methods added by enhancement and abstract methods (methods without any code)
if ( methodName.startsWith( "$$_hibernate_" ) || methodInfo.getCodeAttribute() == null ) {
continue;
}
try {
final CodeIterator itr = methodInfo.getCodeAttribute().iterator();
while ( itr.hasNext() ) {
final int index = itr.next();
final int op = itr.byteAt( index );
if ( op != Opcode.PUTFIELD && op != Opcode.GETFIELD ) {
continue;
}
// only transform access to fields of the entity being enhanced
if ( !managedCtClass.getName().equals( constPool.getFieldrefClassName( itr.u16bitAt( index + 1 ) ) ) ) {
continue;
}
final String fieldName = constPool.getFieldrefName( itr.u16bitAt( index + 1 ) );
final PersistentAttributeAccessMethods attributeMethods = attributeDescriptorMap.get( fieldName );
// its not a field we have enhanced for interception, so skip it
if ( attributeMethods == null ) {
continue;
}
//System.out.printf( "Transforming access to field [%s] from method [%s]%n", fieldName, methodName );
log.debugf( "Transforming access to field [%s] from method [%s]", fieldName, methodName );
if ( op == Opcode.GETFIELD ) {
final int methodIndex = MethodWriter.addMethod( constPool, attributeMethods.getReader() );
itr.writeByte( Opcode.INVOKEVIRTUAL, index );
itr.write16bit( methodIndex, index + 1 );
}
else {
final int methodIndex = MethodWriter.addMethod( constPool, attributeMethods.getWriter() );
itr.writeByte( Opcode.INVOKEVIRTUAL, index );
itr.write16bit( methodIndex, index + 1 );
}
}
methodInfo.getCodeAttribute().setAttribute( MapMaker.make( classPool, methodInfo ) );
}
catch (BadBytecode bb) {
final String msg = String.format(
"Unable to perform field access transformation in method [%s]",
methodName
);
throw new EnhancementException( msg, bb );
}
}
}
private static class PersistentAttributeAccessMethods {
private final CtMethod reader;
private final CtMethod writer;
private PersistentAttributeAccessMethods(CtMethod reader, CtMethod writer) {
this.reader = reader;
this.writer = writer;
}
private CtMethod getReader() {
return reader;
}
private CtMethod getWriter() {
return writer;
}
}
// --- //
/**
* Replace access to fields of entities (for example, entity.field) with a call to the enhanced getter / setter
* (in this example, entity.$$_hibernate_read_field()). It's assumed that the target entity is enhanced as well.
*
* @param aCtClass Class to enhance (not an entity class).
*/
public void extendedEnhancement(CtClass aCtClass) {
final ConstPool constPool = aCtClass.getClassFile().getConstPool();
final ClassPool classPool = aCtClass.getClassPool();
for ( Object oMethod : aCtClass.getClassFile().getMethods() ) {
final MethodInfo methodInfo = (MethodInfo) oMethod;
final String methodName = methodInfo.getName();
// skip methods added by enhancement and abstract methods (methods without any code)
if ( methodName.startsWith( "$$_hibernate_" ) || methodInfo.getCodeAttribute() == null ) {
continue;
}
try {
final CodeIterator itr = methodInfo.getCodeAttribute().iterator();
while ( itr.hasNext() ) {
int index = itr.next();
int op = itr.byteAt( index );
if ( op != Opcode.PUTFIELD && op != Opcode.GETFIELD ) {
continue;
}
String fieldName = constPool.getFieldrefName( itr.u16bitAt( index + 1 ) );
String fieldClassName = constPool.getClassInfo( constPool.getFieldrefClass( itr.u16bitAt( index + 1 ) ) );
CtClass targetCtClass = classPool.getCtClass( fieldClassName );
if ( !enhancementContext.isEntityClass( targetCtClass ) && !enhancementContext.isCompositeClass( targetCtClass ) ) {
continue;
}
if ( targetCtClass == aCtClass
|| !enhancementContext.isPersistentField( targetCtClass.getField( fieldName ) )
|| PersistentAttributesHelper.hasAnnotation( targetCtClass, fieldName, Id.class )
|| "this$0".equals( fieldName ) ) {
continue;
}
log.debugf(
"Extended enhancement: Transforming access to field [%s.%s] from method [%s#%s]",
fieldClassName,
fieldName,
aCtClass.getName(),
methodName
);
if ( op == Opcode.GETFIELD ) {
int fieldReaderMethodIndex = constPool.addMethodrefInfo(
constPool.addClassInfo( fieldClassName ),
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + fieldName,
"()" + constPool.getFieldrefType( itr.u16bitAt( index + 1 ) )
);
itr.writeByte( Opcode.INVOKEVIRTUAL, index );
itr.write16bit( fieldReaderMethodIndex, index + 1 );
}
else {
int fieldWriterMethodIndex = constPool.addMethodrefInfo(
constPool.addClassInfo( fieldClassName ),
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName,
"(" + constPool.getFieldrefType( itr.u16bitAt( index + 1 ) ) + ")V"
);
itr.writeByte( Opcode.INVOKEVIRTUAL, index );
itr.write16bit( fieldWriterMethodIndex, index + 1 );
}
}
methodInfo.getCodeAttribute().setAttribute( MapMaker.make( classPool, methodInfo ) );
}
catch (BadBytecode bb) {
final String msg = String.format(
"Unable to perform extended enhancement in method [%s]",
methodName
);
throw new EnhancementException( msg, bb );
}
catch (NotFoundException nfe) {
final String msg = String.format(
"Unable to perform extended enhancement in method [%s]",
methodName
);
throw new EnhancementException( msg, nfe );
}
}
}
}

View File

@ -0,0 +1,411 @@
/*
* 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.internal.javassist;
import java.beans.Introspector;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Map;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMember;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.SignatureAttribute;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
/**
* util methods to fetch attribute metadata. consistent for both field and property access types.
*
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
* @see org.hibernate.internal.util.ReflectHelper
*/
public class PersistentAttributesHelper {
private PersistentAttributesHelper() {
}
private static final CoreMessageLogger log = CoreLogging.messageLogger( PersistentAttributesHelper.class );
public static boolean hasAnnotation(CtField attribute, Class<? extends Annotation> annotation) {
return getAnnotation( attribute, annotation ) != null;
}
public static boolean hasAnnotation(CtClass ctClass, String attributeName, Class<? extends Annotation> annotation) {
return getAnnotation( ctClass, attributeName, annotation ) != null;
}
public static <T extends Annotation> T getAnnotation(CtField attribute, Class<T> annotation) {
return getAnnotation( attribute.getDeclaringClass(), attribute.getName(), annotation );
}
public static <T extends Annotation> T getAnnotation(CtClass ctClass, String attributeName, Class<T> annotation) {
AccessType classAccessType = getAccessTypeOrNull( ctClass );
CtField field = findFieldOrNull( ctClass, attributeName );
CtMethod getter = findGetterOrNull( ctClass, attributeName );
if ( classAccessType == AccessType.FIELD || ( field != null && getAccessTypeOrNull( field ) == AccessType.FIELD ) ) {
return field == null ? null : getAnnotationOrNull( field, annotation );
}
if ( classAccessType == AccessType.PROPERTY || ( getter != null && getAccessTypeOrNull( getter ) == AccessType.PROPERTY ) ) {
return getter == null ? null : getAnnotationOrNull( getter, annotation );
}
T found = ( getter == null ? null : getAnnotationOrNull( getter, annotation ) );
if ( found == null && field != null ) {
return getAnnotationOrNull( field, annotation );
}
return found;
}
private static <T extends Annotation> T getAnnotationOrNull(CtMember ctMember, Class<T> annotation) {
try {
if ( ctMember.hasAnnotation( annotation ) ) {
return annotation.cast( ctMember.getAnnotation( annotation ) );
}
}
catch (ClassNotFoundException cnfe) {
// should never happen
}
return null;
}
private static AccessType getAccessTypeOrNull(CtMember ctMember) {
Access access = getAnnotationOrNull( ctMember, Access.class );
return access == null ? null : access.value();
}
private static AccessType getAccessTypeOrNull(CtClass ctClass) {
try {
if ( ctClass.hasAnnotation( Access.class ) ) {
return ( (Access) ctClass.getAnnotation( Access.class ) ).value();
}
else {
CtClass extendsClass = ctClass.getSuperclass();
return extendsClass == null ? null : getAccessTypeOrNull( extendsClass );
}
}
catch (ClassNotFoundException e) {
return null;
}
catch (NotFoundException e) {
return null;
}
}
//
/**
* duplicated here to take CtClass instead of Class
* @see org.hibernate.internal.util.ReflectHelper#locateField
*/
private static CtField findFieldOrNull(CtClass ctClass, String propertyName) {
if ( ctClass == null ) {
return null;
}
try {
return ctClass.getField( propertyName );
}
catch ( NotFoundException nsfe ) {
try {
return findFieldOrNull( ctClass.getSuperclass(), propertyName );
}
catch (NotFoundException e) {
return null;
}
}
}
/**
* duplicated here to take CtClass instead of Class
* @see org.hibernate.internal.util.ReflectHelper#findGetterMethod
*/
private static CtMethod findGetterOrNull(CtClass ctClass, String propertyName) {
if ( ctClass == null ) {
return null;
}
CtMethod method = getterOrNull( ctClass, propertyName );
if ( method != null ) {
return method;
}
try {
// check if extends
method = findGetterOrNull( ctClass.getSuperclass(), propertyName );
if ( method != null ) {
return method;
}
// check if implements
for ( CtClass interfaceCtClass : ctClass.getInterfaces() ) {
method = getterOrNull( interfaceCtClass, propertyName );
if ( method != null ) {
return method;
}
}
}
catch (NotFoundException nfe) {
// give up
}
return null;
}
private static CtMethod getterOrNull(CtClass containerClass, String propertyName) {
for ( CtMethod method : containerClass.getDeclaredMethods() ) {
try {
// if the method has parameters, skip it
if ( method.isEmpty() || method.getParameterTypes().length != 0 ) {
continue;
}
}
catch (NotFoundException e) {
continue;
}
final String methodName = method.getName();
// try "get"
if ( methodName.startsWith( "get" ) ) {
String testStdMethod = Introspector.decapitalize( methodName.substring( 3 ) );
String testOldMethod = methodName.substring( 3 );
if ( testStdMethod.equals( propertyName ) || testOldMethod.equals( propertyName ) ) {
return method;
}
}
// if not "get", then try "is"
if ( methodName.startsWith( "is" ) ) {
String testStdMethod = Introspector.decapitalize( methodName.substring( 2 ) );
String testOldMethod = methodName.substring( 2 );
if ( testStdMethod.equals( propertyName ) || testOldMethod.equals( propertyName ) ) {
return method;
}
}
}
return null;
}
//
public static boolean isPossibleBiDirectionalAssociation(CtField persistentField) {
return PersistentAttributesHelper.hasAnnotation( persistentField, OneToOne.class ) ||
PersistentAttributesHelper.hasAnnotation( persistentField, OneToMany.class ) ||
PersistentAttributesHelper.hasAnnotation( persistentField, ManyToOne.class ) ||
PersistentAttributesHelper.hasAnnotation( persistentField, ManyToMany.class );
}
public static String getMappedBy(CtField persistentField, CtClass targetEntity, JavassistEnhancementContext context) throws NotFoundException {
final String local = getMappedByFromAnnotation( persistentField );
if ( local == null || local.isEmpty() ) {
return getMappedByFromTargetEntity( persistentField, targetEntity, context );
}
else {
// HHH-13446 - mappedBy from annotation may not be a valid bi-directional association, verify by calling isValidMappedBy()
return isValidMappedBy( persistentField, targetEntity, local, context ) ? local : "";
}
}
private static boolean isValidMappedBy(CtField persistentField, CtClass targetEntity, String mappedBy, JavassistEnhancementContext context) {
try {
CtField f = targetEntity.getField( mappedBy );
return context.isPersistentField( f ) && isAssignable( persistentField.getDeclaringClass(), inferFieldTypeName( f ) );
}
catch ( NotFoundException e ) {
return false;
}
}
private static String getMappedByFromAnnotation(CtField persistentField) {
OneToOne oto = PersistentAttributesHelper.getAnnotation( persistentField, OneToOne.class );
if ( oto != null ) {
return oto.mappedBy();
}
OneToMany otm = PersistentAttributesHelper.getAnnotation( persistentField, OneToMany.class );
if ( otm != null ) {
return otm.mappedBy();
}
// For @ManyToOne associations, mappedBy must come from the @OneToMany side of the association
ManyToMany mtm = PersistentAttributesHelper.getAnnotation( persistentField, ManyToMany.class );
return mtm == null ? "" : mtm.mappedBy();
}
private static String getMappedByFromTargetEntity(
CtField persistentField,
CtClass targetEntity,
JavassistEnhancementContext context) throws NotFoundException {
// get mappedBy value by searching in the fields of the target entity class
for ( CtField f : targetEntity.getDeclaredFields() ) {
if ( context.isPersistentField( f )
&& getMappedByFromAnnotation( f ).equals( persistentField.getName() )
&& isAssignable( persistentField.getDeclaringClass(), inferFieldTypeName( f ) ) ) {
log.debugf(
"mappedBy association for field [%s#%s] is [%s#%s]",
persistentField.getDeclaringClass().getName(),
persistentField.getName(),
targetEntity.getName(),
f.getName()
);
return f.getName();
}
}
return "";
}
public static CtClass getTargetEntityClass(CtClass managedCtClass, CtField persistentField) throws NotFoundException {
// get targetEntity defined in the annotation
try {
OneToOne oto = PersistentAttributesHelper.getAnnotation( persistentField, OneToOne.class );
OneToMany otm = PersistentAttributesHelper.getAnnotation( persistentField, OneToMany.class );
ManyToOne mto = PersistentAttributesHelper.getAnnotation( persistentField, ManyToOne.class );
ManyToMany mtm = PersistentAttributesHelper.getAnnotation( persistentField, ManyToMany.class );
Class<?> targetClass = null;
if ( oto != null ) {
targetClass = oto.targetEntity();
}
if ( otm != null ) {
targetClass = otm.targetEntity();
}
if ( mto != null ) {
targetClass = mto.targetEntity();
}
if ( mtm != null ) {
targetClass = mtm.targetEntity();
}
if ( targetClass != null && targetClass != void.class ) {
return managedCtClass.getClassPool().get( targetClass.getName() );
}
}
catch (NotFoundException ignore) {
}
// infer targetEntity from generic type signature
String inferredTypeName = inferTypeName( managedCtClass, persistentField.getName() );
return inferredTypeName == null ? null : managedCtClass.getClassPool().get( inferredTypeName );
}
/**
* Consistent with hasAnnotation()
*/
private static String inferTypeName(CtClass ctClass, String attributeName ) {
AccessType classAccessType = getAccessTypeOrNull( ctClass );
CtField field = findFieldOrNull( ctClass, attributeName );
CtMethod getter = findGetterOrNull( ctClass, attributeName );
if ( classAccessType == AccessType.FIELD || ( field != null && getAccessTypeOrNull( field ) == AccessType.FIELD ) ) {
return field == null ? null : inferFieldTypeName( field );
}
if ( classAccessType == AccessType.PROPERTY || ( getter != null && getAccessTypeOrNull( getter ) == AccessType.PROPERTY ) ) {
return getter == null ? null : inferMethodTypeName( getter );
}
String found = ( getter == null ? null : inferMethodTypeName( getter ) );
if ( found == null && field != null ) {
return inferFieldTypeName( field );
}
return found;
}
private static String inferFieldTypeName(CtField field) {
try {
if ( field.getFieldInfo2().getAttribute( SignatureAttribute.tag ) == null ) {
return field.getType().getName();
}
return inferGenericTypeName(
field.getType(),
SignatureAttribute.toTypeSignature( field.getGenericSignature() )
);
}
catch (BadBytecode ignore) {
return null;
}
catch (NotFoundException e) {
return null;
}
}
private static String inferMethodTypeName(CtMethod method) {
try {
if ( method.getMethodInfo2().getAttribute( SignatureAttribute.tag ) == null ) {
return method.getReturnType().getName();
}
return inferGenericTypeName(
method.getReturnType(),
SignatureAttribute.toMethodSignature( method.getGenericSignature() ).getReturnType()
);
}
catch (BadBytecode ignore) {
return null;
}
catch (NotFoundException e) {
return null;
}
}
private static String inferGenericTypeName(CtClass ctClass, SignatureAttribute.Type genericSignature) {
// infer targetEntity from generic type signature
if ( isAssignable( ctClass, Collection.class.getName() ) ) {
return ( (SignatureAttribute.ClassType) genericSignature ).getTypeArguments()[0].getType().jvmTypeName();
}
if ( isAssignable( ctClass, Map.class.getName() ) ) {
return ( (SignatureAttribute.ClassType) genericSignature ).getTypeArguments()[1].getType().jvmTypeName();
}
return ctClass.getName();
}
//
public static boolean isAssignable(CtClass thisCtClass, String targetClassName) {
if ( thisCtClass == null ) {
return false;
}
if ( thisCtClass.getName().equals( targetClassName ) ) {
return true;
}
try {
// check if extends
if ( isAssignable( thisCtClass.getSuperclass(), targetClassName ) ) {
return true;
}
// check if implements
for ( CtClass interfaceCtClass : thisCtClass.getInterfaces() ) {
if ( isAssignable( interfaceCtClass, targetClassName ) ) {
return true;
}
}
}
catch (NotFoundException e) {
// keep going
}
return false;
}
public static boolean isAssignable(CtField thisCtField, String targetClassName) {
try {
return isAssignable( thisCtField.getType(), targetClassName );
}
catch (NotFoundException e) {
// keep going
}
return false;
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.internal.javassist;
import java.lang.annotation.Annotation;
import javassist.CtClass;
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
public class UnloadedCtClass implements UnloadedClass {
private final CtClass ctClass;
public UnloadedCtClass(CtClass ctClass) {
this.ctClass = ctClass;
}
@Override
public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
return ctClass.hasAnnotation( annotationType );
}
@Override
public String getName() {
return ctClass.getName();
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.internal.javassist;
import java.lang.annotation.Annotation;
import javassist.CtField;
import org.hibernate.bytecode.enhance.spi.UnloadedField;
public class UnloadedCtField implements UnloadedField {
final CtField ctField;
public UnloadedCtField(CtField ctField) {
this.ctField = ctField;
}
@Override
public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
return ctField.hasAnnotation( annotationType );
}
@Override
public String toString() {
return this.ctField.toString();
}
}

View File

@ -0,0 +1,11 @@
/*
* 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 containing bytecode enhancement code (internals)
*/
package org.hibernate.bytecode.enhance.internal.javassist;

View File

@ -0,0 +1,101 @@
/*
* 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.internal.javassist;
import java.io.Serializable;
import org.hibernate.PropertyAccessException;
import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.cfg.AvailableSettings;
/**
* The {@link org.hibernate.bytecode.spi.ReflectionOptimizer.AccessOptimizer} implementation for Javassist
* which simply acts as an adapter to the {@link BulkAccessor} class.
*
* @author Steve Ebersole
*/
public class AccessOptimizerAdapter implements ReflectionOptimizer.AccessOptimizer, Serializable {
private static final String PROPERTY_GET_EXCEPTION = String.format(
"exception getting property value with Javassist (set %s to false for more info)",
AvailableSettings.USE_REFLECTION_OPTIMIZER
);
private static final String PROPERTY_SET_EXCEPTION = String.format(
"exception setting property value with Javassist (set %s to false for more info)",
AvailableSettings.USE_REFLECTION_OPTIMIZER
);
private final BulkAccessor bulkAccessor;
private final Class mappedClass;
/**
* Constructs an AccessOptimizerAdapter
*
* @param bulkAccessor The bulk accessor to use
* @param mappedClass The mapped class
*/
public AccessOptimizerAdapter(BulkAccessor bulkAccessor, Class mappedClass) {
this.bulkAccessor = bulkAccessor;
this.mappedClass = mappedClass;
}
@Override
public String[] getPropertyNames() {
return bulkAccessor.getGetters();
}
@Override
public Object[] getPropertyValues(Object object) {
try {
return bulkAccessor.getPropertyValues( object );
}
catch ( Throwable t ) {
throw new PropertyAccessException(
t,
PROPERTY_GET_EXCEPTION,
false,
mappedClass,
getterName( t, bulkAccessor )
);
}
}
@Override
public void setPropertyValues(Object object, Object[] values) {
try {
bulkAccessor.setPropertyValues( object, values );
}
catch ( Throwable t ) {
throw new PropertyAccessException(
t,
PROPERTY_SET_EXCEPTION,
true,
mappedClass,
setterName( t, bulkAccessor )
);
}
}
private static String setterName(Throwable t, BulkAccessor accessor) {
if (t instanceof BulkAccessorException ) {
return accessor.getSetters()[ ( (BulkAccessorException) t ).getIndex() ];
}
else {
return "?";
}
}
private static String getterName(Throwable t, BulkAccessor accessor) {
if (t instanceof BulkAccessorException ) {
return accessor.getGetters()[ ( (BulkAccessorException) t ).getIndex() ];
}
else {
return "?";
}
}
}

View File

@ -0,0 +1,109 @@
/*
* 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.internal.javassist;
import java.io.Serializable;
/**
* A JavaBean bulk accessor, which provides methods capable of getting/setting multiple properties
* of a JavaBean at once.
*
* IMPORTANT NOTE!!! Apparently the order of the methods here is important as I think BulkAccessorFactory
* makes use of that information in terms of accessing the constructor. Anyway, when I tried to re-arrange them
* the BulkAccessor creation failed and tests started to fail.
*
* @author Muga Nishizawa
* @author Shigeru Chiba
*/
public abstract class BulkAccessor implements Serializable {
protected Class target;
protected String[] getters, setters;
protected Class[] types;
/**
* Protected access constructor so the generated class has access to it.
*/
protected BulkAccessor() {
}
/**
* Obtains the values of properties of a given bean.
*
* @param bean JavaBean.
* @param values the obtained values are stored in this array.
*/
public abstract void getPropertyValues(Object bean, Object[] values);
/**
* Sets properties of a given bean to specified values.
*
* @param bean JavaBean.
* @param values the values assinged to properties.
*/
public abstract void setPropertyValues(Object bean, Object[] values);
/**
* Returns the values of properties of a given bean.
*
* @param bean JavaBean.
*
* @return The property values
*/
public Object[] getPropertyValues(Object bean) {
final Object[] values = new Object[getters.length];
getPropertyValues( bean, values );
return values;
}
/**
* Returns the types of properties.
*
* @return The property types
*/
public Class[] getPropertyTypes() {
return types.clone();
}
/**
* Returns the setter names of properties.
*
* @return The getter names
*/
public String[] getGetters() {
return getters.clone();
}
/**
* Returns the getter names of the properties.
*
* @return The setter names
*/
public String[] getSetters() {
return setters.clone();
}
/**
* Creates a new instance of <code>BulkAccessor</code>.
* The created instance provides methods for setting/getting
* specified properties at once.
*
* @param beanClass the class of the JavaBeans accessed
* through the created object.
* @param getters the names of setter methods for specified properties.
* @param setters the names of getter methods for specified properties.
* @param types the types of specified properties.
*
* @return The created BulkAccessor
*/
public static BulkAccessor create(
Class beanClass,
String[] getters,
String[] setters,
Class[] types) {
return new BulkAccessorFactory( beanClass, getters, setters, types ).create();
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.internal.javassist;
import org.hibernate.HibernateException;
/**
* An exception thrown while generating a bulk accessor.
*
* @author Muga Nishizawa
* @author modified by Shigeru Chiba
*/
public class BulkAccessorException extends HibernateException {
private final int index;
/**
* Constructs an exception.
*
* @param message Message explaining the exception condition
*/
public BulkAccessorException(String message) {
this( message, -1 );
}
/**
* Constructs an exception.
*
* @param message Message explaining the exception condition
* @param index The index of the property that causes an exception.
*/
public BulkAccessorException(String message, int index) {
this( message, index, null );
}
/**
* Constructs an exception.
*
* @param message Message explaining the exception condition
* @param cause The underlying cause
*/
public BulkAccessorException(String message, Exception cause) {
this( message, -1, cause );
}
/**
* Constructs an exception.
*
* @param message Message explaining the exception condition
* @param index The index of the property that causes an exception.
* @param cause The underlying cause
*/
public BulkAccessorException(String message, int index, Exception cause) {
super( message + " : @" + index, cause );
this.index = index;
}
/**
* Returns the index of the property that causes this exception.
*
* @return -1 if the index is not specified.
*/
public int getIndex() {
return this.index;
}
}

View File

@ -0,0 +1,423 @@
/*
* 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.internal.javassist;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import javassist.CannotCompileException;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.Bytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.bytecode.StackMapTable;
import javassist.util.proxy.FactoryHelper;
import javassist.util.proxy.RuntimeSupport;
/**
* A factory of bulk accessors.
*
* @author Muga Nishizawa
* @author modified by Shigeru Chiba
*/
class BulkAccessorFactory {
private static final String PACKAGE_NAME_PREFIX = "org.javassist.tmp.";
private static final String BULKACESSOR_CLASS_NAME = BulkAccessor.class.getName();
private static final String OBJECT_CLASS_NAME = Object.class.getName();
private static final String GENERATED_GETTER_NAME = "getPropertyValues";
private static final String GENERATED_SETTER_NAME = "setPropertyValues";
private static final String GET_SETTER_DESC = "(Ljava/lang/Object;[Ljava/lang/Object;)V";
private static final String THROWABLE_CLASS_NAME = Throwable.class.getName();
private static final String BULKEXCEPTION_CLASS_NAME = BulkAccessorException.class.getName();
private static int counter;
private Class targetBean;
private String[] getterNames;
private String[] setterNames;
private Class[] types;
public String writeDirectory;
BulkAccessorFactory(
Class target,
String[] getterNames,
String[] setterNames,
Class[] types) {
this.targetBean = target;
this.getterNames = getterNames;
this.setterNames = setterNames;
this.types = types;
this.writeDirectory = null;
}
BulkAccessor create() {
final Method[] getters = new Method[getterNames.length];
final Method[] setters = new Method[setterNames.length];
findAccessors( targetBean, getterNames, setterNames, types, getters, setters );
final Class beanClass;
try {
final ClassFile classfile = make( getters, setters );
final ClassLoader loader = this.getClassLoader();
if ( writeDirectory != null ) {
FactoryHelper.writeFile( classfile, writeDirectory );
}
beanClass = FactoryHelper.toClass( classfile, null, loader, getDomain() );
return (BulkAccessor) this.newInstance( beanClass );
}
catch ( Exception e ) {
throw new BulkAccessorException( e.getMessage(), e );
}
}
private ProtectionDomain getDomain() {
final Class cl;
if ( this.targetBean != null ) {
cl = this.targetBean;
}
else {
cl = this.getClass();
}
return cl.getProtectionDomain();
}
private ClassFile make(Method[] getters, Method[] setters) throws CannotCompileException {
String className = targetBean.getName();
// set the name of bulk accessor.
className = className + "_$$_bulkaccess_" + counter++;
if ( className.startsWith( "java." ) ) {
className = PACKAGE_NAME_PREFIX + className;
}
final ClassFile classfile = new ClassFile( false, className, BULKACESSOR_CLASS_NAME );
classfile.setAccessFlags( AccessFlag.PUBLIC );
addDefaultConstructor( classfile );
addGetter( classfile, getters );
addSetter( classfile, setters );
return classfile;
}
private ClassLoader getClassLoader() {
if ( targetBean != null && targetBean.getName().equals( OBJECT_CLASS_NAME ) ) {
return targetBean.getClassLoader();
}
else {
return getClass().getClassLoader();
}
}
private Object newInstance(Class type) throws Exception {
final BulkAccessor instance = (BulkAccessor) type.newInstance();
instance.target = targetBean;
final int len = getterNames.length;
instance.getters = new String[len];
instance.setters = new String[len];
instance.types = new Class[len];
for ( int i = 0; i < len; i++ ) {
instance.getters[i] = getterNames[i];
instance.setters[i] = setterNames[i];
instance.types[i] = types[i];
}
return instance;
}
/**
* Declares a constructor that takes no parameter.
*
* @param classfile The class descriptor
*
* @throws CannotCompileException Indicates trouble with the underlying Javassist calls
*/
private void addDefaultConstructor(ClassFile classfile) throws CannotCompileException {
final ConstPool constPool = classfile.getConstPool();
final String constructorSignature = "()V";
final MethodInfo constructorMethodInfo = new MethodInfo( constPool, MethodInfo.nameInit, constructorSignature );
final Bytecode code = new Bytecode( constPool, 0, 1 );
// aload_0
code.addAload( 0 );
// invokespecial
code.addInvokespecial( BulkAccessor.class.getName(), MethodInfo.nameInit, constructorSignature );
// return
code.addOpcode( Opcode.RETURN );
constructorMethodInfo.setCodeAttribute( code.toCodeAttribute() );
constructorMethodInfo.setAccessFlags( AccessFlag.PUBLIC );
classfile.addMethod( constructorMethodInfo );
}
private void addGetter(ClassFile classfile, final Method[] getters) throws CannotCompileException {
final ConstPool constPool = classfile.getConstPool();
final int targetBeanConstPoolIndex = constPool.addClassInfo( this.targetBean.getName() );
final String desc = GET_SETTER_DESC;
final MethodInfo getterMethodInfo = new MethodInfo( constPool, GENERATED_GETTER_NAME, desc );
final Bytecode code = new Bytecode( constPool, 6, 4 );
/* | this | bean | args | raw bean | */
if ( getters.length >= 0 ) {
// aload_1 // load bean
code.addAload( 1 );
// checkcast // cast bean
code.addCheckcast( this.targetBean.getName() );
// astore_3 // store bean
code.addAstore( 3 );
for ( int i = 0; i < getters.length; ++i ) {
if ( getters[i] != null ) {
final Method getter = getters[i];
// aload_2 // args
code.addAload( 2 );
// iconst_i // continue to aastore
// growing stack is 1
code.addIconst( i );
final Class returnType = getter.getReturnType();
int typeIndex = -1;
if ( returnType.isPrimitive() ) {
typeIndex = FactoryHelper.typeIndex( returnType );
// new
code.addNew( FactoryHelper.wrapperTypes[typeIndex] );
// dup
code.addOpcode( Opcode.DUP );
}
// aload_3 // load the raw bean
code.addAload( 3 );
final String getterSignature = RuntimeSupport.makeDescriptor( getter );
final String getterName = getter.getName();
if ( this.targetBean.isInterface() ) {
// invokeinterface
code.addInvokeinterface( targetBeanConstPoolIndex, getterName, getterSignature, 1 );
}
else {
// invokevirtual
code.addInvokevirtual( targetBeanConstPoolIndex, getterName, getterSignature );
}
if ( typeIndex >= 0 ) {
// is a primitive type
// invokespecial
code.addInvokespecial(
FactoryHelper.wrapperTypes[typeIndex],
MethodInfo.nameInit,
FactoryHelper.wrapperDesc[typeIndex]
);
}
// aastore // args
code.add( Opcode.AASTORE );
code.growStack( -3 );
}
}
}
// return
code.addOpcode( Opcode.RETURN );
getterMethodInfo.setCodeAttribute( code.toCodeAttribute() );
getterMethodInfo.setAccessFlags( AccessFlag.PUBLIC );
classfile.addMethod( getterMethodInfo );
}
private void addSetter(ClassFile classfile, final Method[] setters) throws CannotCompileException {
final ConstPool constPool = classfile.getConstPool();
final int targetTypeConstPoolIndex = constPool.addClassInfo( this.targetBean.getName() );
final String desc = GET_SETTER_DESC;
final MethodInfo setterMethodInfo = new MethodInfo( constPool, GENERATED_SETTER_NAME, desc );
final Bytecode code = new Bytecode( constPool, 4, 6 );
StackMapTable stackmap = null;
/* | this | bean | args | i | raw bean | exception | */
if ( setters.length > 0 ) {
// required to exception table
int start;
int end;
// iconst_0 // i
code.addIconst( 0 );
// istore_3 // store i
code.addIstore( 3 );
// aload_1 // load the bean
code.addAload( 1 );
// checkcast // cast the bean into a raw bean
code.addCheckcast( this.targetBean.getName() );
// astore 4 // store the raw bean
code.addAstore( 4 );
/* current stack len = 0 */
// start region to handling exception (BulkAccessorException)
start = code.currentPc();
int lastIndex = 0;
for ( int i = 0; i < setters.length; ++i ) {
if ( setters[i] != null ) {
final int diff = i - lastIndex;
if ( diff > 0 ) {
// iinc 3, 1
code.addOpcode( Opcode.IINC );
code.add( 3 );
code.add( diff );
lastIndex = i;
}
}
/* current stack len = 0 */
// aload 4 // load the raw bean
code.addAload( 4 );
// aload_2 // load the args
code.addAload( 2 );
// iconst_i
code.addIconst( i );
// aaload
code.addOpcode( Opcode.AALOAD );
// checkcast
final Class[] setterParamTypes = setters[i].getParameterTypes();
final Class setterParamType = setterParamTypes[0];
if ( setterParamType.isPrimitive() ) {
// checkcast (case of primitive type)
// invokevirtual (case of primitive type)
this.addUnwrapper( code, setterParamType );
}
else {
// checkcast (case of reference type)
code.addCheckcast( setterParamType.getName() );
}
/* current stack len = 2 */
final String rawSetterMethodDesc = RuntimeSupport.makeDescriptor( setters[i] );
if ( !this.targetBean.isInterface() ) {
// invokevirtual
code.addInvokevirtual( targetTypeConstPoolIndex, setters[i].getName(), rawSetterMethodDesc );
}
else {
// invokeinterface
final Class[] params = setters[i].getParameterTypes();
int size;
if ( params[0].equals( Double.TYPE ) || params[0].equals( Long.TYPE ) ) {
size = 3;
}
else {
size = 2;
}
code.addInvokeinterface( targetTypeConstPoolIndex, setters[i].getName(), rawSetterMethodDesc, size );
}
}
// end region to handling exception (BulkAccessorException)
end = code.currentPc();
// return
code.addOpcode( Opcode.RETURN );
/* current stack len = 0 */
// register in exception table
final int throwableTypeIndex = constPool.addClassInfo( THROWABLE_CLASS_NAME );
final int handlerPc = code.currentPc();
code.addExceptionHandler( start, end, handlerPc, throwableTypeIndex );
// astore 5 // store exception
code.addAstore( 5 );
// new // BulkAccessorException
code.addNew( BULKEXCEPTION_CLASS_NAME );
// dup
code.addOpcode( Opcode.DUP );
// aload 5 // load exception
code.addAload( 5 );
// iload_3 // i
code.addIload( 3 );
// invokespecial // BulkAccessorException.<init>
final String consDesc = "(Ljava/lang/Throwable;I)V";
code.addInvokespecial( BULKEXCEPTION_CLASS_NAME, MethodInfo.nameInit, consDesc );
// athrow
code.addOpcode( Opcode.ATHROW );
final StackMapTable.Writer writer = new StackMapTable.Writer(32);
final int[] localTags = {
StackMapTable.OBJECT,
StackMapTable.OBJECT,
StackMapTable.OBJECT,
StackMapTable.INTEGER
};
final int[] localData = {
constPool.getThisClassInfo(),
constPool.addClassInfo( "java/lang/Object" ),
constPool.addClassInfo( "[Ljava/lang/Object;" ),
0
};
final int[] stackTags = {
StackMapTable.OBJECT
};
final int[] stackData = {
throwableTypeIndex
};
writer.fullFrame( handlerPc, localTags, localData, stackTags, stackData );
stackmap = writer.toStackMapTable( constPool );
}
else {
// return
code.addOpcode( Opcode.RETURN );
}
final CodeAttribute ca = code.toCodeAttribute();
if ( stackmap != null ) {
ca.setAttribute( stackmap );
}
setterMethodInfo.setCodeAttribute( ca );
setterMethodInfo.setAccessFlags( AccessFlag.PUBLIC );
classfile.addMethod( setterMethodInfo );
}
private void addUnwrapper(Bytecode code, Class type) {
final int index = FactoryHelper.typeIndex( type );
final String wrapperType = FactoryHelper.wrapperTypes[index];
// checkcast
code.addCheckcast( wrapperType );
// invokevirtual
code.addInvokevirtual( wrapperType, FactoryHelper.unwarpMethods[index], FactoryHelper.unwrapDesc[index] );
}
private static void findAccessors(
Class clazz,
String[] getterNames,
String[] setterNames,
Class[] types,
Method[] getters,
Method[] setters) {
final int length = types.length;
if ( setterNames.length != length || getterNames.length != length ) {
throw new BulkAccessorException( "bad number of accessors" );
}
final Class[] getParam = new Class[0];
final Class[] setParam = new Class[1];
for ( int i = 0; i < length; i++ ) {
if ( getterNames[i] != null ) {
final Method getter = findAccessor( clazz, getterNames[i], getParam, i );
if ( getter.getReturnType() != types[i] ) {
throw new BulkAccessorException( "wrong return type: " + getterNames[i], i );
}
getters[i] = getter;
}
if ( setterNames[i] != null ) {
setParam[0] = types[i];
setters[i] = findAccessor( clazz, setterNames[i], setParam, i );
}
}
}
@SuppressWarnings("unchecked")
private static Method findAccessor(Class clazz, String name, Class[] params, int index)
throws BulkAccessorException {
try {
final Method method = clazz.getDeclaredMethod( name, params );
if ( Modifier.isPrivate( method.getModifiers() ) ) {
throw new BulkAccessorException( "private property", index );
}
return method;
}
catch ( NoSuchMethodException e ) {
throw new BulkAccessorException( "cannot find an accessor", index );
}
}
}

View File

@ -0,0 +1,102 @@
/*
* 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.internal.javassist;
import java.lang.reflect.Modifier;
import org.hibernate.bytecode.enhance.internal.javassist.EnhancerImpl;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.jboss.logging.Logger;
/**
* Bytecode provider implementation for Javassist.
*
* @author Steve Ebersole
*/
public class BytecodeProviderImpl implements BytecodeProvider {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
BytecodeProviderImpl.class.getName()
);
@Override
public ProxyFactoryFactory getProxyFactoryFactory() {
return new ProxyFactoryFactoryImpl();
}
@Override
public ReflectionOptimizer getReflectionOptimizer(
Class clazz,
String[] getterNames,
String[] setterNames,
Class[] types) {
FastClass fastClass;
BulkAccessor bulkAccessor;
try {
fastClass = FastClass.create( clazz );
bulkAccessor = BulkAccessor.create( clazz, getterNames, setterNames, types );
if ( !clazz.isInterface() && !Modifier.isAbstract( clazz.getModifiers() ) ) {
if ( fastClass == null ) {
bulkAccessor = null;
}
else {
//test out the optimizer:
final Object instance = fastClass.newInstance();
bulkAccessor.setPropertyValues( instance, bulkAccessor.getPropertyValues( instance ) );
}
}
}
catch ( Throwable t ) {
fastClass = null;
bulkAccessor = null;
if ( LOG.isDebugEnabled() ) {
int index = 0;
if (t instanceof BulkAccessorException) {
index = ( (BulkAccessorException) t ).getIndex();
}
if ( index >= 0 ) {
LOG.debugf(
"Reflection optimizer disabled for %s [%s: %s (property %s)]",
clazz.getName(),
StringHelper.unqualify( t.getClass().getName() ),
t.getMessage(),
setterNames[index]
);
}
else {
LOG.debugf(
"Reflection optimizer disabled for %s [%s: %s]",
clazz.getName(),
StringHelper.unqualify( t.getClass().getName() ),
t.getMessage()
);
}
}
}
if ( fastClass != null && bulkAccessor != null ) {
return new ReflectionOptimizerImpl(
new InstantiationOptimizerAdapter( fastClass ),
new AccessOptimizerAdapter( bulkAccessor, clazz )
);
}
return null;
}
@Override
public Enhancer getEnhancer(EnhancementContext enhancementContext) {
return new EnhancerImpl( enhancementContext );
}
}

View File

@ -0,0 +1,247 @@
/*
* 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.internal.javassist;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* Fast access to class information
*
* @author Muga Nishizawa
*/
public class FastClass implements Serializable {
private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
private final Class type;
/**
* Constructs a FastClass
*
* @param type The class to optimize
*
* @return The fast class access to the given class
*/
public static FastClass create(Class type) {
return new FastClass( type );
}
private FastClass(Class type) {
this.type = type;
}
/**
* Access to invoke a method on the class that this fast class handles
*
* @param name The name of the method to invoke,
* @param parameterTypes The method parameter types
* @param obj The instance on which to invoke the method
* @param args The parameter arguments
*
* @return The method result
*
* @throws InvocationTargetException Indicates a problem performing invocation
*/
public Object invoke(
String name,
Class[] parameterTypes,
Object obj,
Object[] args) throws InvocationTargetException {
return this.invoke( this.getIndex( name, parameterTypes ), obj, args );
}
/**
* Access to invoke a method on the class that this fast class handles by its index
*
* @param index The method index
* @param obj The instance on which to invoke the method
* @param args The parameter arguments
*
* @return The method result
*
* @throws InvocationTargetException Indicates a problem performing invocation
*/
public Object invoke(
int index,
Object obj,
Object[] args) throws InvocationTargetException {
final Method[] methods = this.type.getMethods();
try {
return methods[index].invoke( obj, args );
}
catch ( ArrayIndexOutOfBoundsException e ) {
throw new IllegalArgumentException(
"Cannot find matching method/constructor"
);
}
catch ( IllegalAccessException e ) {
throw new InvocationTargetException( e );
}
}
/**
* Invoke the default constructor
*
* @return The constructed instance
*
* @throws InvocationTargetException Indicates a problem performing invocation
*/
public Object newInstance() throws InvocationTargetException {
return this.newInstance( this.getIndex( EMPTY_CLASS_ARRAY ), null );
}
/**
* Invoke a parameterized constructor
*
* @param parameterTypes The parameter types
* @param args The parameter arguments to pass along
*
* @return The constructed instance
*
* @throws InvocationTargetException Indicates a problem performing invocation
*/
public Object newInstance(
Class[] parameterTypes,
Object[] args) throws InvocationTargetException {
return this.newInstance( this.getIndex( parameterTypes ), args );
}
/**
* Invoke a constructor by its index
*
* @param index The constructor index
* @param args The parameter arguments to pass along
*
* @return The constructed instance
*
* @throws InvocationTargetException Indicates a problem performing invocation
*/
public Object newInstance(
int index,
Object[] args) throws InvocationTargetException {
final Constructor[] constructors = this.type.getConstructors();
try {
return constructors[index].newInstance( args );
}
catch ( ArrayIndexOutOfBoundsException e ) {
throw new IllegalArgumentException( "Cannot find matching method/constructor" );
}
catch ( InstantiationException e ) {
throw new InvocationTargetException( e );
}
catch ( IllegalAccessException e ) {
throw new InvocationTargetException( e );
}
}
/**
* Locate the index of a method
*
* @param name The method name
* @param parameterTypes The method parameter types
*
* @return The index
*/
public int getIndex(String name, Class[] parameterTypes) {
final Method[] methods = this.type.getMethods();
boolean eq;
for ( int i = 0; i < methods.length; ++i ) {
if ( !Modifier.isPublic( methods[i].getModifiers() ) ) {
continue;
}
if ( !methods[i].getName().equals( name ) ) {
continue;
}
final Class[] params = methods[i].getParameterTypes();
if ( params.length != parameterTypes.length ) {
continue;
}
eq = true;
for ( int j = 0; j < params.length; ++j ) {
if ( !params[j].equals( parameterTypes[j] ) ) {
eq = false;
break;
}
}
if ( eq ) {
return i;
}
}
return -1;
}
/**
* Locate the index of a constructor
*
* @param parameterTypes The constructor parameter types
*
* @return The index
*/
public int getIndex(Class[] parameterTypes) {
final Constructor[] constructors = this.type.getConstructors();
boolean eq;
for ( int i = 0; i < constructors.length; ++i ) {
if ( !Modifier.isPublic( constructors[i].getModifiers() ) ) {
continue;
}
final Class[] params = constructors[i].getParameterTypes();
if ( params.length != parameterTypes.length ) {
continue;
}
eq = true;
for ( int j = 0; j < params.length; ++j ) {
if ( !params[j].equals( parameterTypes[j] ) ) {
eq = false;
break;
}
}
if ( eq ) {
return i;
}
}
return -1;
}
/**
* Get the wrapped class name
*
* @return The class name
*/
public String getName() {
return this.type.getName();
}
/**
* Get the wrapped java class reference
*
* @return The class reference
*/
public Class getJavaClass() {
return this.type;
}
@Override
public String toString() {
return this.type.toString();
}
@Override
public int hashCode() {
return this.type.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof FastClass
&& this.type.equals( ((FastClass) o).type );
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.internal.javassist;
import java.io.Serializable;
import org.hibernate.InstantiationException;
import org.hibernate.bytecode.spi.ReflectionOptimizer;
/**
* The {@link org.hibernate.bytecode.spi.ReflectionOptimizer.InstantiationOptimizer} implementation for Javassist
* which simply acts as an adapter to the {@link FastClass} class.
*
* @author Steve Ebersole
*/
public class InstantiationOptimizerAdapter implements ReflectionOptimizer.InstantiationOptimizer, Serializable {
private final FastClass fastClass;
/**
* Constructs the InstantiationOptimizerAdapter
*
* @param fastClass The fast class for the class to be instantiated here.
*/
public InstantiationOptimizerAdapter(FastClass fastClass) {
this.fastClass = fastClass;
}
@Override
public Object newInstance() {
try {
return fastClass.newInstance();
}
catch ( Exception e ) {
throw new InstantiationException(
"Could not instantiate entity with Javassist optimizer: ",
fastClass.getJavaClass(),
e
);
}
}
}

View File

@ -0,0 +1,147 @@
/*
* 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.internal.javassist;
import java.lang.reflect.Method;
import java.util.HashMap;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.Proxy;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.bytecode.spi.BasicProxyFactory;
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.proxy.ProxyFactory;
import org.hibernate.proxy.pojo.javassist.JavassistProxyFactory;
/**
* A factory for Javassist-based {@link ProxyFactory} instances.
*
* @author Steve Ebersole
*/
public class ProxyFactoryFactoryImpl implements ProxyFactoryFactory {
/**
* Builds a Javassist-based proxy factory.
*
* @return a new Javassist-based proxy factory.
*/
@Override
public ProxyFactory buildProxyFactory(SessionFactoryImplementor sessionFactory) {
return new JavassistProxyFactory();
}
/**
* Constructs a BasicProxyFactoryImpl
*
* @param superClass The abstract super class (or null if none).
* @param interfaces Interfaces to be proxied (or null if none).
*
* @return The constructed BasicProxyFactoryImpl
*/
@Override
public BasicProxyFactory buildBasicProxyFactory(Class superClass, Class[] interfaces) {
return new BasicProxyFactoryImpl( superClass, interfaces );
}
private static class BasicProxyFactoryImpl implements BasicProxyFactory {
private final Class proxyClass;
public BasicProxyFactoryImpl(Class superClass, Class[] interfaces) {
if ( superClass == null && ( interfaces == null || interfaces.length < 1 ) ) {
throw new AssertionFailure( "attempting to build proxy without any superclass or interfaces" );
}
final javassist.util.proxy.ProxyFactory factory = new javassist.util.proxy.ProxyFactory();
factory.setFilter( FINALIZE_FILTER );
if ( superClass != null ) {
factory.setSuperclass( superClass );
}
if ( interfaces != null && interfaces.length > 0 ) {
factory.setInterfaces( interfaces );
}
proxyClass = factory.createClass();
}
public Object getProxy() {
try {
final Proxy proxy = (Proxy) proxyClass.newInstance();
proxy.setHandler( new PassThroughHandler( proxy, proxyClass.getName() ) );
return proxy;
}
catch ( Throwable t ) {
throw new HibernateException( "Unable to instantiated proxy instance" );
}
}
public boolean isInstance(Object object) {
return proxyClass.isInstance( object );
}
}
private static final MethodFilter FINALIZE_FILTER = new MethodFilter() {
public boolean isHandled(Method m) {
// skip finalize methods
return !( m.getParameterCount() == 0 && m.getName().equals( "finalize" ) );
}
};
private static class PassThroughHandler implements MethodHandler {
private HashMap data = new HashMap();
private final Object proxiedObject;
private final String proxiedClassName;
public PassThroughHandler(Object proxiedObject, String proxiedClassName) {
this.proxiedObject = proxiedObject;
this.proxiedClassName = proxiedClassName;
}
@SuppressWarnings("unchecked")
public Object invoke(
Object object,
Method method,
Method method1,
Object[] args) throws Exception {
final String name = method.getName();
if ( "toString".equals( name ) ) {
return proxiedClassName + "@" + System.identityHashCode( object );
}
else if ( "equals".equals( name ) ) {
return proxiedObject == object;
}
else if ( "hashCode".equals( name ) ) {
return System.identityHashCode( object );
}
final boolean hasGetterSignature = method.getParameterCount() == 0
&& method.getReturnType() != null;
final boolean hasSetterSignature = method.getParameterCount() == 1
&& ( method.getReturnType() == null || method.getReturnType() == void.class );
if ( name.startsWith( "get" ) && hasGetterSignature ) {
final String propName = name.substring( 3 );
return data.get( propName );
}
else if ( name.startsWith( "is" ) && hasGetterSignature ) {
final String propName = name.substring( 2 );
return data.get( propName );
}
else if ( name.startsWith( "set" ) && hasSetterSignature) {
final String propName = name.substring( 3 );
data.put( propName, args[0] );
return null;
}
else {
// todo : what else to do here?
return null;
}
}
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.internal.javassist;
import java.io.Serializable;
import org.hibernate.bytecode.spi.ReflectionOptimizer;
/**
* ReflectionOptimizer implementation for Javassist.
*
* @author Steve Ebersole
*/
public class ReflectionOptimizerImpl implements ReflectionOptimizer, Serializable {
private final InstantiationOptimizer instantiationOptimizer;
private final AccessOptimizer accessOptimizer;
/**
* Constructs a ReflectionOptimizerImpl
*
* @param instantiationOptimizer The instantiation optimizer to use
* @param accessOptimizer The property access optimizer to use.
*/
public ReflectionOptimizerImpl(
InstantiationOptimizer instantiationOptimizer,
AccessOptimizer accessOptimizer) {
this.instantiationOptimizer = instantiationOptimizer;
this.accessOptimizer = accessOptimizer;
}
@Override
public InstantiationOptimizer getInstantiationOptimizer() {
return instantiationOptimizer;
}
@Override
public AccessOptimizer getAccessOptimizer() {
return accessOptimizer;
}
}

View File

@ -0,0 +1,11 @@
/*
* 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>.
*/
/**
* Javassist support internals
*/
package org.hibernate.bytecode.internal.javassist;

View File

@ -14,7 +14,7 @@ import org.hibernate.service.Service;
* An interface for factories of {@link ProxyFactory proxy factory} instances.
* <p/>
* Currently used to abstract from the tuplizer whether we are using Byte Buddy or
* not for lazy proxy generation.
* Javassist for lazy proxy generation.
*
* @author Steve Ebersole
*/

View File

@ -1255,7 +1255,7 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
/**
* Pick which bytecode enhancing library to use. Currently only supports bytebuddy.
* Pick which bytecode enhancing library to use. Currently supports javassist and bytebuddy, bytebuddy being the default since version 5.3.
*/
String BYTECODE_PROVIDER = "hibernate.bytecode.provider";

View File

@ -327,6 +327,7 @@ public final class Environment implements AvailableSettings {
}
public static final String BYTECODE_PROVIDER_NAME_JAVASSIST = "javassist";
public static final String BYTECODE_PROVIDER_NAME_BYTEBUDDY = "bytebuddy";
public static final String BYTECODE_PROVIDER_NAME_NONE = "none";
public static final String BYTECODE_PROVIDER_NAME_DEFAULT = BYTECODE_PROVIDER_NAME_BYTEBUDDY;
@ -344,6 +345,10 @@ public final class Environment implements AvailableSettings {
return new org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl();
}
if ( BYTECODE_PROVIDER_NAME_JAVASSIST.equals( providerName ) ) {
return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
}
LOG.bytecodeProvider( providerName );
// there is no need to support plugging in a custom BytecodeProvider via FQCN:

View File

@ -397,6 +397,7 @@ class PropertyContainer {
private static boolean mustBeSkipped(XProperty property) {
//TODO make those hardcoded tests more portable (through the bytecode provider?)
return property.isAnnotationPresent( Transient.class )
|| "net.sf.cglib.transform.impl.InterceptFieldCallback".equals( property.getType().getName() );
|| "net.sf.cglib.transform.impl.InterceptFieldCallback".equals( property.getType().getName() )
|| "org.hibernate.bytecode.internal.javassist.FieldHandler".equals( property.getType().getName() );
}
}

View File

@ -159,8 +159,13 @@ public class StandardPojoEntityRepresentationStrategy implements EntityRepresent
PersistentClass bootDescriptor,
BytecodeProvider bytecodeProvider,
RuntimeModelCreationContext creationContext) {
final Set<Class> proxyInterfaces = new java.util.HashSet<>();
/*
* We need to preserve the order of the interfaces they were put into the set, since javassist will choose the
* first one's class-loader to construct the proxy class with. This is also the reason why HibernateProxy.class
* should be the last one in the order (on JBossAS7 its class-loader will be org.hibernate module's class-
* loader, which will not see the classes inside deployed apps. See HHH-3078
*/
final Set<Class> proxyInterfaces = new java.util.LinkedHashSet<>();
final Class mappedClass = mappedJtd.getJavaType();
Class proxyInterface;

View File

@ -38,7 +38,13 @@ public final class ProxyFactoryHelper {
}
public static Set<Class> extractProxyInterfaces(final PersistentClass persistentClass, final String entityName) {
final Set<Class> proxyInterfaces = new java.util.HashSet<>();
/*
* We need to preserve the order of the interfaces they were put into the set, since javassist will choose the
* first one's class-loader to construct the proxy class with. This is also the reason why HibernateProxy.class
* should be the last one in the order (on JBossAS7 its class-loader will be org.hibernate module's class-
* loader, which will not see the classes inside deployed apps. See HHH-3078
*/
final Set<Class> proxyInterfaces = new java.util.LinkedHashSet<Class>();
final Class mappedClass = persistentClass.getMappedClass();
final Class proxyInterface = persistentClass.getProxyInterface();

View File

@ -0,0 +1,134 @@
/*
* 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.proxy.pojo.javassist;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javassist.util.proxy.MethodHandler;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.proxy.pojo.BasicLazyInitializer;
import org.hibernate.type.CompositeType;
import static org.hibernate.internal.CoreLogging.messageLogger;
/**
* A Javassist-based lazy initializer proxy.
*
* @author Muga Nishizawa
*/
public class JavassistLazyInitializer extends BasicLazyInitializer implements MethodHandler {
private static final CoreMessageLogger LOG = messageLogger( JavassistLazyInitializer.class );
private final Class[] interfaces;
private boolean constructed;
public JavassistLazyInitializer(
String entityName,
Class persistentClass,
Class[] interfaces,
Object id,
Method getIdentifierMethod,
Method setIdentifierMethod,
CompositeType componentIdType,
SharedSessionContractImplementor session,
boolean overridesEquals) {
super( entityName, persistentClass, id, getIdentifierMethod, setIdentifierMethod, componentIdType, session, overridesEquals );
this.interfaces = interfaces;
}
protected void constructed() {
constructed = true;
}
@Override
public Object invoke(
final Object proxy,
final Method thisMethod,
final Method proceed,
final Object[] args) throws Throwable {
if ( this.constructed ) {
// HHH-10922 - Internal calls to bytecode enhanced methods cause proxy to be initialized
if ( thisMethod.getName().startsWith( "$$_hibernate_" ) ) {
return proceed.invoke( proxy, args );
}
Object result;
try {
result = this.invoke( thisMethod, args, proxy );
}
catch ( Throwable t ) {
throw t instanceof RuntimeException ? t : new Exception( t.getCause() );
}
if ( result == INVOKE_IMPLEMENTATION ) {
Object target = getImplementation();
final Object returnValue;
try {
if ( ReflectHelper.isPublic( persistentClass, thisMethod ) ) {
if ( !thisMethod.getDeclaringClass().isInstance( target ) ) {
throw new ClassCastException(
target.getClass().getName()
+ " incompatible with "
+ thisMethod.getDeclaringClass().getName()
);
}
returnValue = thisMethod.invoke( target, args );
}
else {
thisMethod.setAccessible( true );
returnValue = thisMethod.invoke( target, args );
}
if ( returnValue == target ) {
if ( returnValue.getClass().isInstance( proxy ) ) {
return proxy;
}
else {
LOG.narrowingProxy( returnValue.getClass() );
}
}
return returnValue;
}
catch ( InvocationTargetException ite ) {
throw ite.getTargetException();
}
}
else {
return result;
}
}
else {
// while constructor is running
if ( thisMethod.getName().equals( "getHibernateLazyInitializer" ) ) {
return this;
}
else {
return proceed.invoke( proxy, args );
}
}
}
@Override
protected Object serializableProxy() {
return new SerializableProxy(
getEntityName(),
persistentClass,
interfaces,
getIdentifier(),
( isReadOnlySettingAvailable() ? Boolean.valueOf( isReadOnly() ) : isReadOnlyBeforeAttachedToSession() ),
getSessionFactoryUuid(),
isAllowLoadOutsideTransaction(),
getIdentifierMethod,
setIdentifierMethod,
componentIdType
);
}
}

View File

@ -0,0 +1,224 @@
/*
* 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.proxy.pojo.javassist;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Set;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.Proxy;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.ProxyFactory;
import org.hibernate.type.CompositeType;
import static org.hibernate.internal.CoreLogging.messageLogger;
/**
* A {@link ProxyFactory} implementation for producing Javassist-based proxies.
*
* @author Muga Nishizawa
*/
public class JavassistProxyFactory implements ProxyFactory, Serializable {
private static final CoreMessageLogger LOG = messageLogger( JavassistProxyFactory.class );
private static final MethodFilter EXCLUDE_FILTER = m -> {
// skip finalize methods and Groovy getMetaClass
return !(
m.getParameterCount() == 0 && m.getName().equals( "finalize" ) || (
m.getName().equals( "getMetaClass" ) &&
m.getReturnType().getName().equals( "groovy.lang.MetaClass" )
)
);
};
private Class persistentClass;
private String entityName;
private Class[] interfaces;
private Method getIdentifierMethod;
private Method setIdentifierMethod;
private CompositeType componentIdType;
private boolean overridesEquals;
private Class proxyClass;
public JavassistProxyFactory() {
}
@Override
public void postInstantiate(
final String entityName,
final Class persistentClass,
final Set<Class> interfaces,
final Method getIdentifierMethod,
final Method setIdentifierMethod,
CompositeType componentIdType) throws HibernateException {
this.entityName = entityName;
this.persistentClass = persistentClass;
this.interfaces = toArray( interfaces );
this.getIdentifierMethod = getIdentifierMethod;
this.setIdentifierMethod = setIdentifierMethod;
this.componentIdType = componentIdType;
this.overridesEquals = ReflectHelper.overridesEquals( persistentClass );
this.proxyClass = buildJavassistProxyFactory().createClass();
}
private Class[] toArray(Set<Class> interfaces) {
if ( interfaces == null ) {
return ArrayHelper.EMPTY_CLASS_ARRAY;
}
return interfaces.toArray( new Class[interfaces.size()] );
}
private javassist.util.proxy.ProxyFactory buildJavassistProxyFactory() {
return buildJavassistProxyFactory(
persistentClass,
interfaces
);
}
public static javassist.util.proxy.ProxyFactory buildJavassistProxyFactory(
final Class persistentClass,
final Class[] interfaces) {
javassist.util.proxy.ProxyFactory factory = new javassist.util.proxy.ProxyFactory() {
@Override
protected ClassLoader getClassLoader() {
return persistentClass.getClassLoader();
}
};
factory.setSuperclass( interfaces.length == 1 ? persistentClass : null );
factory.setInterfaces( interfaces );
factory.setFilter( EXCLUDE_FILTER );
return factory;
}
@Override
public HibernateProxy getProxy(
Object id,
SharedSessionContractImplementor session) throws HibernateException {
final JavassistLazyInitializer initializer = new JavassistLazyInitializer(
entityName,
persistentClass,
interfaces,
id,
getIdentifierMethod,
setIdentifierMethod,
componentIdType,
session,
overridesEquals
);
try {
final HibernateProxy proxy = (HibernateProxy) proxyClass.getConstructor().newInstance();
( (Proxy) proxy ).setHandler( initializer );
initializer.constructed();
return proxy;
}
catch (NoSuchMethodException e) {
String logMessage = LOG.bytecodeEnhancementFailedBecauseOfDefaultConstructor( entityName );
LOG.error( logMessage, e );
throw new HibernateException( logMessage, e );
}
catch (Throwable t) {
String logMessage = LOG.bytecodeEnhancementFailed( entityName );
LOG.error( logMessage, t );
throw new HibernateException( logMessage, t );
}
}
public static HibernateProxy deserializeProxy(SerializableProxy serializableProxy) {
final JavassistLazyInitializer initializer = new JavassistLazyInitializer(
serializableProxy.getEntityName(),
serializableProxy.getPersistentClass(),
serializableProxy.getInterfaces(),
serializableProxy.getId(),
resolveIdGetterMethod( serializableProxy ),
resolveIdSetterMethod( serializableProxy ),
serializableProxy.getComponentIdType(),
null,
ReflectHelper.overridesEquals( serializableProxy.getPersistentClass() )
);
final javassist.util.proxy.ProxyFactory factory = buildJavassistProxyFactory(
serializableProxy.getPersistentClass(),
serializableProxy.getInterfaces()
);
// note: interface is assumed to already contain HibernateProxy.class
try {
final Class proxyClass = factory.createClass();
final HibernateProxy proxy = ( HibernateProxy ) proxyClass.newInstance();
( (Proxy) proxy ).setHandler( initializer );
initializer.constructed();
return proxy;
}
catch ( Throwable t ) {
final String message = LOG.bytecodeEnhancementFailed( serializableProxy.getEntityName() );
LOG.error( message, t );
throw new HibernateException( message, t );
}
}
@SuppressWarnings("unchecked")
private static Method resolveIdGetterMethod(SerializableProxy serializableProxy) {
if ( serializableProxy.getIdentifierGetterMethodName() == null ) {
return null;
}
try {
return serializableProxy.getIdentifierGetterMethodClass().getDeclaredMethod( serializableProxy.getIdentifierGetterMethodName() );
}
catch (NoSuchMethodException e) {
throw new HibernateException(
String.format(
Locale.ENGLISH,
"Unable to deserialize proxy [%s, %s]; could not locate id getter method [%s] on entity class [%s]",
serializableProxy.getEntityName(),
serializableProxy.getId(),
serializableProxy.getIdentifierGetterMethodName(),
serializableProxy.getIdentifierGetterMethodClass()
)
);
}
}
@SuppressWarnings("unchecked")
private static Method resolveIdSetterMethod(SerializableProxy serializableProxy) {
if ( serializableProxy.getIdentifierSetterMethodName() == null ) {
return null;
}
try {
return serializableProxy.getIdentifierSetterMethodClass().getDeclaredMethod(
serializableProxy.getIdentifierSetterMethodName(),
serializableProxy.getIdentifierSetterMethodParams()
);
}
catch (NoSuchMethodException e) {
throw new HibernateException(
String.format(
Locale.ENGLISH,
"Unable to deserialize proxy [%s, %s]; could not locate id setter method [%s] on entity class [%s]",
serializableProxy.getEntityName(),
serializableProxy.getId(),
serializableProxy.getIdentifierSetterMethodName(),
serializableProxy.getIdentifierSetterMethodClass()
)
);
}
}
}

View File

@ -0,0 +1,141 @@
/*
* 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.proxy.pojo.javassist;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.hibernate.proxy.AbstractSerializableProxy;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.type.CompositeType;
/**
* Serializable placeholder for Javassist proxies
*/
public final class SerializableProxy extends AbstractSerializableProxy {
private final Class persistentClass;
private final Class[] interfaces;
private final String identifierGetterMethodName;
private final Class identifierGetterMethodClass;
private final String identifierSetterMethodName;
private final Class identifierSetterMethodClass;
private final Class[] identifierSetterMethodParams;
private final CompositeType componentIdType;
/**
* @deprecated use {@link #SerializableProxy(String, Class, Class[], Object, Boolean, String, boolean, Method, Method, CompositeType)} instead.
*/
@Deprecated
public SerializableProxy(
String entityName,
Class persistentClass,
Class[] interfaces,
Serializable id,
Boolean readOnly,
Method getIdentifierMethod,
Method setIdentifierMethod,
CompositeType componentIdType) {
this(
entityName, persistentClass, interfaces, id, readOnly, null, false,
getIdentifierMethod, setIdentifierMethod, componentIdType
);
}
public SerializableProxy(
String entityName,
Class persistentClass,
Class[] interfaces,
Object id,
Boolean readOnly,
String sessionFactoryUuid,
boolean allowLoadOutsideTransaction,
Method getIdentifierMethod,
Method setIdentifierMethod,
CompositeType componentIdType) {
super( entityName, id, readOnly, sessionFactoryUuid, allowLoadOutsideTransaction );
this.persistentClass = persistentClass;
this.interfaces = interfaces;
if ( getIdentifierMethod != null ) {
identifierGetterMethodName = getIdentifierMethod.getName();
identifierGetterMethodClass = getIdentifierMethod.getDeclaringClass();
}
else {
identifierGetterMethodName = null;
identifierGetterMethodClass = null;
}
if ( setIdentifierMethod != null ) {
identifierSetterMethodName = setIdentifierMethod.getName();
identifierSetterMethodClass = setIdentifierMethod.getDeclaringClass();
identifierSetterMethodParams = setIdentifierMethod.getParameterTypes();
}
else {
identifierSetterMethodName = null;
identifierSetterMethodClass = null;
identifierSetterMethodParams = null;
}
this.componentIdType = componentIdType;
}
@Override
protected String getEntityName() {
return super.getEntityName();
}
@Override
protected Object getId() {
return super.getId();
}
protected Class getPersistentClass() {
return persistentClass;
}
protected Class[] getInterfaces() {
return interfaces;
}
protected String getIdentifierGetterMethodName() {
return identifierGetterMethodName;
}
protected Class getIdentifierGetterMethodClass() {
return identifierGetterMethodClass;
}
protected String getIdentifierSetterMethodName() {
return identifierSetterMethodName;
}
protected Class getIdentifierSetterMethodClass() {
return identifierSetterMethodClass;
}
protected Class[] getIdentifierSetterMethodParams() {
return identifierSetterMethodParams;
}
protected CompositeType getComponentIdType() {
return componentIdType;
}
/**
* Deserialization hook. This method is called by JDK deserialization. We use this hook
* to replace the serial form with a live form.
*
* @return The live form.
*/
private Object readResolve() {
HibernateProxy proxy = JavassistProxyFactory.deserializeProxy( this );
afterDeserialization( ( JavassistLazyInitializer ) proxy.getHibernateLazyInitializer() );
return proxy;
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.tool.instrument.javassist;
import org.hibernate.internal.log.DeprecationLogger;
import org.hibernate.tool.enhance.EnhancementTask;
/**
* This is the legacy Ant-task Hibernate provided historically to
* perform its old-school bytecode instrumentation. That has been replaced wholesale
* with a new approach to bytecode manipulation offering 3 build-time variations for Ant,
* Maven and Gradle.
*
* @author Muga Nishizawa
* @author Steve Ebersole
*
* @deprecated This is the legacy Ant-task Hibernate provided historically to
* perform its old-school bytecode instrumentation. That has been replaced wholesale
* with a new approach to bytecode manipulation offering 3 build-time variations for Ant,
* Maven and Gradle.
*
* @see EnhancementTask
*/
@Deprecated
@SuppressWarnings("unused")
public class InstrumentTask extends EnhancementTask {
public InstrumentTask() {
DeprecationLogger.DEPRECATION_LOGGER.logDeprecatedInstrumentTask( InstrumentTask.class, EnhancementTask.class );
}
}

View File

@ -23,6 +23,7 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.proxy.ProxyFactory;
import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyFactory;
import org.hibernate.proxy.pojo.javassist.JavassistProxyFactory;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.logger.LoggerInspectionRule;
@ -103,6 +104,9 @@ public class PrivateConstructorTest extends BaseEntityManagerFunctionalTestCase
if ( byteCodeProvider == null || Environment.BYTECODE_PROVIDER_NAME_BYTEBUDDY.equals( byteCodeProvider ) ) {
return ByteBuddyProxyFactory.class;
}
else if ( Environment.BYTECODE_PROVIDER_NAME_JAVASSIST.equals( byteCodeProvider ) ) {
return JavassistProxyFactory.class;
}
else {
throw new UnsupportedOperationException( "Unknown bytecode provider:" + byteCodeProvider );
}

View File

@ -9,6 +9,7 @@ package org.hibernate.test.bytecode;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.hibernate.bytecode.internal.javassist.BulkAccessor;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.cfg.Environment;
@ -20,6 +21,15 @@ import org.junit.Test;
* @author Steve Ebersole
*/
public class ReflectionOptimizerTest extends BaseUnitTestCase {
@Test
public void testBulkAccessorDirectly() {
BulkAccessor bulkAccessor = BulkAccessor.create(
Bean.class,
BeanReflectionHelper.getGetterNames(),
BeanReflectionHelper.getSetterNames(),
BeanReflectionHelper.getTypes()
);
}
@Test
public void testReflectionOptimization() {

View File

@ -56,7 +56,7 @@ public class BidirectionalLazyGroupsInEmbeddableTest extends BaseCoreFunctionalT
}
@Test
@Ignore("Test is failing with ByteBuddy if the mappings are moved to the fields.")
@Ignore("Test is failing with Javassist and also fails with ByteBuddy if the mappings are moved to the fields.")
public void test() {
doInHibernate(

View File

@ -19,7 +19,7 @@ import org.junit.Assert;
import org.junit.Test;
/**
* Tests if bytebuddy instrumentation is done with the proper classloader for entities with proxy class. The classloader
* Tests if javassist instrumentation is done with the proper classloader for entities with proxy class. The classloader
* of {@link HibernateProxy} will not see {@link IPerson}, since it is only accessible from this package. But: the
* classloader of {@link IPerson} will see {@link HibernateProxy}, so instrumentation will only work if this classloader
* is chosen for creating the instrumented proxy class. We need to check the class of a loaded object though, since

View File

@ -0,0 +1,67 @@
/*
* 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.test.bytecode.enhancement.javassist;
import java.io.FileNotFoundException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import javassist.CtClass;
import org.hibernate.bytecode.enhance.internal.javassist.EnhancerImpl;
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
/**
* @author Vlad Mihalcea
*/
public class EnhancerFileNotFoundTest {
@Test
@TestForIssue( jiraKey = "HHH-11307" )
public void test() throws Exception {
Enhancer enhancer = new Enhancer( new DefaultEnhancementContext() );
try {
String resourceName = Hidden.class.getName().replace( '.', '/' ) + ".class";
URL url = getClass().getClassLoader().getResource( resourceName );
if ( url != null ) {
Files.delete( Paths.get( url.toURI() ) );
enhancer.loadCtClassFromClass( Hidden.class );
}
fail( "Should throw FileNotFoundException!" );
} catch ( Exception expected ) {
assertSame( FileNotFoundException.class, expected.getCause().getClass() );
}
}
// --- //
private static class Enhancer extends EnhancerImpl {
public Enhancer(EnhancementContext enhancementContext) {
super( enhancementContext );
}
@Override
// change visibility protected -> public
public CtClass loadCtClassFromClass(Class<?> aClass) {
return super.loadCtClassFromClass( aClass );
}
}
// --- //
private static class Hidden {
}
}

View File

@ -77,7 +77,7 @@ public abstract class EntityTools {
return null;
}
else if ( HibernateProxy.class.isAssignableFrom( clazz ) ) {
// Get the source class of Bytebuddy proxy instance.
// Get the source class of Javassist proxy instance.
return (Class<T>) clazz.getSuperclass();
}
return clazz;

View File

@ -7,7 +7,8 @@
module org.hibernate.orm.integrationtest.java.module {
exports org.hibernate.orm.integrationtest.java.module.service;
opens org.hibernate.orm.integrationtest.java.module.entity to
org.hibernate.orm.core;
org.hibernate.orm.core,
javassist; // Necessary for javassist, but not for bytebuddy (the default)
requires java.persistence;
/*

View File

@ -168,7 +168,8 @@ public class OsgiIntegrationTest {
// // and use defined imports instead
// probe.setHeader(
// Constants.IMPORT_PACKAGE,
// "javax.persistence"
// "javassist.util.proxy"
// + ",javax.persistence"
// + ",javax.persistence.spi"
// + ",org.h2"
// + ",org.osgi.framework"

View File

@ -169,6 +169,7 @@ public class DialectFilterExtension implements ExecutionCondition {
}
return dialectAccess.get().getDialect();
}
return emScope.get().getDialect();
}

View File

@ -43,5 +43,5 @@ public @interface RequiresDialect {
/**
* the Dialect version
*/
int version();
int version() default -1;
}