HHH-11152: Added BytecodeProvider based on Byte Buddy
This commit is contained in:
parent
31a60b84c6
commit
ba3359fe62
|
@ -154,6 +154,7 @@ subprojects { subProject ->
|
||||||
|
|
||||||
testRuntime( libraries.log4j )
|
testRuntime( libraries.log4j )
|
||||||
testRuntime( libraries.javassist )
|
testRuntime( libraries.javassist )
|
||||||
|
testRuntime( libraries.byteBuddy )
|
||||||
testRuntime( libraries.woodstox )
|
testRuntime( libraries.woodstox )
|
||||||
|
|
||||||
//Databases
|
//Databases
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
org.hibernate.cfg,
|
org.hibernate.cfg,
|
||||||
org.hibernate.service,
|
org.hibernate.service,
|
||||||
javax.persistence;version="[1.0.0,2.1.0]",
|
javax.persistence;version="[1.0.0,2.1.0]",
|
||||||
<!-- Needed for proxying's Javassist enhancement during runtime -->
|
<!-- Needed for proxy enhancement during runtime -->
|
||||||
org.hibernate.proxy,
|
org.hibernate.proxy,
|
||||||
javassist.util.proxy,
|
javassist.util.proxy,
|
||||||
*
|
*
|
||||||
|
|
|
@ -23,6 +23,7 @@ configurations {
|
||||||
dependencies {
|
dependencies {
|
||||||
compile( libraries.jpa )
|
compile( libraries.jpa )
|
||||||
compile( libraries.javassist )
|
compile( libraries.javassist )
|
||||||
|
compile( libraries.byteBuddy )
|
||||||
compile( libraries.antlr )
|
compile( libraries.antlr )
|
||||||
compile( libraries.jta )
|
compile( libraries.jta )
|
||||||
compile( libraries.jandex )
|
compile( libraries.jandex )
|
||||||
|
@ -95,6 +96,7 @@ dependencies {
|
||||||
testRuntime( libraries.expression_language_impl )
|
testRuntime( libraries.expression_language_impl )
|
||||||
testRuntime( 'jaxen:jaxen:1.1' )
|
testRuntime( 'jaxen:jaxen:1.1' )
|
||||||
testRuntime( libraries.javassist )
|
testRuntime( libraries.javassist )
|
||||||
|
testRuntime( libraries.byteBuddy )
|
||||||
|
|
||||||
testCompile( project( ':hibernate-jpamodelgen' ) )
|
testCompile( project( ':hibernate-jpamodelgen' ) )
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,330 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
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 org.hibernate.bytecode.enhance.spi.EnhancementException;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
|
import org.hibernate.internal.CoreLogging;
|
||||||
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.annotation.AnnotationDescription;
|
||||||
|
import net.bytebuddy.description.annotation.AnnotationValue;
|
||||||
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.scaffold.FieldLocator;
|
||||||
|
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
|
||||||
|
import net.bytebuddy.implementation.Implementation;
|
||||||
|
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
|
||||||
|
import net.bytebuddy.jar.asm.MethodVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import net.bytebuddy.jar.asm.Type;
|
||||||
|
|
||||||
|
class BiDirectionalAssociationHandler implements Implementation {
|
||||||
|
|
||||||
|
private static final CoreMessageLogger log = CoreLogging.messageLogger( BiDirectionalAssociationHandler.class );
|
||||||
|
|
||||||
|
static Implementation wrap(
|
||||||
|
TypeDescription managedCtClass,
|
||||||
|
ByteBuddyEnhancementContext enhancementContext,
|
||||||
|
FieldDescription persistentField,
|
||||||
|
Implementation implementation) {
|
||||||
|
if ( !enhancementContext.doBiDirectionalAssociationManagement( persistentField ) ) {
|
||||||
|
return implementation;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeDescription targetEntity = getTargetEntityClass( managedCtClass, persistentField );
|
||||||
|
if ( targetEntity == null ) {
|
||||||
|
return implementation;
|
||||||
|
}
|
||||||
|
String mappedBy = getMappedBy( persistentField, targetEntity, enhancementContext );
|
||||||
|
if ( mappedBy == null || mappedBy.isEmpty() ) {
|
||||||
|
log.infof(
|
||||||
|
"Could not find bi-directional association for field [%s#%s]",
|
||||||
|
managedCtClass.getName(),
|
||||||
|
persistentField.getName()
|
||||||
|
);
|
||||||
|
return implementation;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeDescription targetType = FieldLocator.ForClassHierarchy.Factory.INSTANCE.make( targetEntity )
|
||||||
|
.locate( mappedBy )
|
||||||
|
.getField()
|
||||||
|
.getType()
|
||||||
|
.asErasure();
|
||||||
|
|
||||||
|
if ( EnhancerImpl.isAnnotationPresent( persistentField, OneToOne.class ) ) {
|
||||||
|
implementation = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldValue.class, persistentField )
|
||||||
|
.bind( CodeTemplates.MappedBy.class, mappedBy )
|
||||||
|
.to( CodeTemplates.OneToOneHandler.class )
|
||||||
|
.wrap( implementation );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( EnhancerImpl.isAnnotationPresent( persistentField, OneToMany.class ) ) {
|
||||||
|
implementation = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldValue.class, persistentField )
|
||||||
|
.bind( CodeTemplates.MappedBy.class, mappedBy )
|
||||||
|
.to( persistentField.getType().asErasure().isAssignableTo( Map.class )
|
||||||
|
? CodeTemplates.OneToManyOnMapHandler.class
|
||||||
|
: CodeTemplates.OneToManyOnCollectionHandler.class )
|
||||||
|
.wrap( implementation );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( EnhancerImpl.isAnnotationPresent( persistentField, ManyToOne.class ) ) {
|
||||||
|
implementation = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldValue.class, persistentField )
|
||||||
|
.bind( CodeTemplates.MappedBy.class, mappedBy )
|
||||||
|
.to( CodeTemplates.ManyToOneHandler.class )
|
||||||
|
.wrap( implementation );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( EnhancerImpl.isAnnotationPresent( persistentField, ManyToMany.class ) ) {
|
||||||
|
|
||||||
|
if ( persistentField.getType().asErasure().isAssignableTo( Map.class ) || targetType.isAssignableTo( Map.class ) ) {
|
||||||
|
log.infof(
|
||||||
|
"Bi-directional association for field [%s#%s] not managed: @ManyToMany in java.util.Map attribute not supported ",
|
||||||
|
managedCtClass.getName(),
|
||||||
|
persistentField.getName()
|
||||||
|
);
|
||||||
|
return implementation;
|
||||||
|
}
|
||||||
|
|
||||||
|
implementation = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldValue.class, persistentField )
|
||||||
|
.bind( CodeTemplates.MappedBy.class, mappedBy )
|
||||||
|
.to( CodeTemplates.ManyToManyHandler.class )
|
||||||
|
.wrap( implementation );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BiDirectionalAssociationHandler( implementation, targetEntity, targetType, mappedBy );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TypeDescription getTargetEntityClass(TypeDescription managedCtClass, FieldDescription persistentField) {
|
||||||
|
try {
|
||||||
|
AnnotationDescription.Loadable<OneToOne> oto = EnhancerImpl.getAnnotation( persistentField, OneToOne.class );
|
||||||
|
AnnotationDescription.Loadable<OneToMany> otm = EnhancerImpl.getAnnotation( persistentField, OneToMany.class );
|
||||||
|
AnnotationDescription.Loadable<ManyToOne> mto = EnhancerImpl.getAnnotation( persistentField, ManyToOne.class );
|
||||||
|
AnnotationDescription.Loadable<ManyToMany> mtm = EnhancerImpl.getAnnotation( persistentField, ManyToMany.class );
|
||||||
|
|
||||||
|
if ( oto == null && otm == null && mto == null && mtm == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotationValue<?, ?> targetClass = null;
|
||||||
|
if ( oto != null ) {
|
||||||
|
targetClass = oto.getValue( new MethodDescription.ForLoadedMethod( OneToOne.class.getDeclaredMethod( "targetEntity" ) ) );
|
||||||
|
}
|
||||||
|
if ( otm != null ) {
|
||||||
|
targetClass = otm.getValue( new MethodDescription.ForLoadedMethod( OneToMany.class.getDeclaredMethod( "targetEntity" ) ) );
|
||||||
|
}
|
||||||
|
if ( mto != null ) {
|
||||||
|
targetClass = mto.getValue( new MethodDescription.ForLoadedMethod( ManyToOne.class.getDeclaredMethod( "targetEntity" ) ) );
|
||||||
|
}
|
||||||
|
if ( mtm != null ) {
|
||||||
|
targetClass = mtm.getValue( new MethodDescription.ForLoadedMethod( ManyToMany.class.getDeclaredMethod( "targetEntity" ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( targetClass == null ) {
|
||||||
|
log.infof(
|
||||||
|
"Could not find type of bi-directional association for field [%s#%s]",
|
||||||
|
managedCtClass.getName(),
|
||||||
|
persistentField.getName()
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if ( !targetClass.resolve( TypeDescription.class ).represents( void.class ) ) {
|
||||||
|
return targetClass.resolve( TypeDescription.class );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return entityType( target( persistentField ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeDescription.Generic target(FieldDescription persistentField) {
|
||||||
|
AnnotationDescription.Loadable<Access> access = persistentField.getDeclaringType().asErasure().getDeclaredAnnotations().ofType( Access.class );
|
||||||
|
if ( access != null && access.loadSilent().value() == AccessType.FIELD ) {
|
||||||
|
return persistentField.getType();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MethodDescription getter = EnhancerImpl.getterOf( persistentField );
|
||||||
|
if ( getter == null ) {
|
||||||
|
return persistentField.getType();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return getter.getReturnType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getMappedBy(FieldDescription target, TypeDescription targetEntity, ByteBuddyEnhancementContext context) {
|
||||||
|
String mappedBy = getMappedByNotManyToMany( target );
|
||||||
|
if ( mappedBy == null || mappedBy.isEmpty() ) {
|
||||||
|
return getMappedByManyToMany( target, targetEntity, context );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return mappedBy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getMappedByNotManyToMany(FieldDescription target) {
|
||||||
|
try {
|
||||||
|
AnnotationDescription.Loadable<OneToOne> oto = EnhancerImpl.getAnnotation( target, OneToOne.class );
|
||||||
|
if ( oto != null ) {
|
||||||
|
return oto.getValue( new MethodDescription.ForLoadedMethod( OneToOne.class.getDeclaredMethod( "mappedBy" ) ) ).resolve( String.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotationDescription.Loadable<OneToMany> otm = EnhancerImpl.getAnnotation( target, OneToMany.class );
|
||||||
|
if ( otm != null ) {
|
||||||
|
return otm.getValue( new MethodDescription.ForLoadedMethod( OneToMany.class.getDeclaredMethod( "mappedBy" ) ) ).resolve( String.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotationDescription.Loadable<ManyToMany> mtm = EnhancerImpl.getAnnotation( target, ManyToMany.class );
|
||||||
|
if ( mtm != null ) {
|
||||||
|
return mtm.getValue( new MethodDescription.ForLoadedMethod( ManyToMany.class.getDeclaredMethod( "mappedBy" ) ) ).resolve( String.class );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getMappedByManyToMany(FieldDescription target, TypeDescription targetEntity, ByteBuddyEnhancementContext context) {
|
||||||
|
for ( FieldDescription f : targetEntity.getDeclaredFields() ) {
|
||||||
|
if ( context.isPersistentField( f )
|
||||||
|
&& target.getName().equals( getMappedByNotManyToMany( f ) )
|
||||||
|
&& target.getDeclaringType().asErasure().isAssignableTo( entityType( f.getType() ) ) ) {
|
||||||
|
log.debugf(
|
||||||
|
"mappedBy association for field [%s#%s] is [%s#%s]",
|
||||||
|
target.getDeclaringType().asErasure().getName(),
|
||||||
|
target.getName(),
|
||||||
|
targetEntity.getName(),
|
||||||
|
f.getName()
|
||||||
|
);
|
||||||
|
return f.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeDescription entityType(TypeDescription.Generic type) {
|
||||||
|
if ( type.getSort().isParameterized() ) {
|
||||||
|
if ( type.asErasure().isAssignableTo( Collection.class ) ) {
|
||||||
|
return type.getTypeArguments().get( 0 ).asErasure();
|
||||||
|
}
|
||||||
|
if ( type.asErasure().isAssignableTo( Map.class ) ) {
|
||||||
|
return type.getTypeArguments().get( 1 ).asErasure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return type.asErasure();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Implementation delegate;
|
||||||
|
|
||||||
|
private final TypeDescription targetEntity;
|
||||||
|
|
||||||
|
private final TypeDescription targetType;
|
||||||
|
|
||||||
|
private final String mappedBy;
|
||||||
|
|
||||||
|
private BiDirectionalAssociationHandler(
|
||||||
|
Implementation delegate,
|
||||||
|
TypeDescription targetEntity,
|
||||||
|
TypeDescription targetType,
|
||||||
|
String mappedBy) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.targetEntity = targetEntity;
|
||||||
|
this.targetType = targetType;
|
||||||
|
this.mappedBy = mappedBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteCodeAppender appender(Target implementationTarget) {
|
||||||
|
return new WrappingAppender( delegate.appender( implementationTarget ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstrumentedType prepare(InstrumentedType instrumentedType) {
|
||||||
|
return delegate.prepare( instrumentedType );
|
||||||
|
}
|
||||||
|
|
||||||
|
private class WrappingAppender implements ByteCodeAppender {
|
||||||
|
|
||||||
|
private final ByteCodeAppender delegate;
|
||||||
|
|
||||||
|
private WrappingAppender(ByteCodeAppender delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size apply(
|
||||||
|
MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
|
||||||
|
return delegate.apply( new MethodVisitor( Opcodes.ASM5, methodVisitor ) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||||
|
if ( owner.startsWith( Type.getInternalName( CodeTemplates.class ) ) ) {
|
||||||
|
if ( name.equals( "getter" ) ) {
|
||||||
|
super.visitTypeInsn( Opcodes.CHECKCAST, targetEntity.getInternalName() );
|
||||||
|
super.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
targetEntity.getInternalName(),
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + mappedBy,
|
||||||
|
Type.getMethodDescriptor( Type.getType( targetType.getDescriptor() ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if ( name.equals( "setterSelf" ) ) {
|
||||||
|
super.visitInsn( Opcodes.POP );
|
||||||
|
super.visitTypeInsn( Opcodes.CHECKCAST, targetEntity.getInternalName() );
|
||||||
|
super.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
super.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
targetEntity.getInternalName(),
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + mappedBy,
|
||||||
|
Type.getMethodDescriptor( Type.getType( void.class ), Type.getType( targetType.getDescriptor() ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if ( name.equals( "setterNull" ) ) {
|
||||||
|
super.visitInsn( Opcodes.POP );
|
||||||
|
super.visitTypeInsn( Opcodes.CHECKCAST, targetEntity.getInternalName() );
|
||||||
|
super.visitInsn( Opcodes.ACONST_NULL );
|
||||||
|
super.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
targetEntity.getInternalName(),
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + mappedBy,
|
||||||
|
Type.getMethodDescriptor( Type.getType( void.class ), Type.getType( targetType.getDescriptor() ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new EnhancementException( "Unknown template method: " + name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitMethodInsn( opcode, owner, name, desc, itf );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, implementationContext, instrumentedMethod );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.bytebuddy;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedField;
|
||||||
|
|
||||||
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
|
||||||
|
class ByteBuddyEnhancementContext {
|
||||||
|
|
||||||
|
private final EnhancementContext enhancementContext;
|
||||||
|
|
||||||
|
ByteBuddyEnhancementContext(EnhancementContext enhancementContext) {
|
||||||
|
this.enhancementContext = enhancementContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassLoader getLoadingClassLoader() {
|
||||||
|
return enhancementContext.getLoadingClassLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEntityClass(TypeDescription classDescriptor) {
|
||||||
|
return enhancementContext.isEntityClass( new UnloadedTypeDescription( classDescriptor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompositeClass(TypeDescription classDescriptor) {
|
||||||
|
return enhancementContext.isCompositeClass( new UnloadedTypeDescription( classDescriptor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMappedSuperclassClass(TypeDescription classDescriptor) {
|
||||||
|
return enhancementContext.isMappedSuperclassClass( new UnloadedTypeDescription( classDescriptor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean doBiDirectionalAssociationManagement(FieldDescription field) {
|
||||||
|
return enhancementContext.doBiDirectionalAssociationManagement( new UnloadedFieldDescription( field ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean doDirtyCheckingInline(TypeDescription classDescriptor) {
|
||||||
|
return enhancementContext.doDirtyCheckingInline( new UnloadedTypeDescription( classDescriptor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean doExtendedEnhancement(TypeDescription classDescriptor) {
|
||||||
|
return enhancementContext.doExtendedEnhancement( new UnloadedTypeDescription( classDescriptor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasLazyLoadableAttributes(TypeDescription classDescriptor) {
|
||||||
|
return enhancementContext.hasLazyLoadableAttributes( new UnloadedTypeDescription( classDescriptor ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPersistentField(FieldDescription ctField) {
|
||||||
|
return enhancementContext.isPersistentField( new UnloadedFieldDescription( ctField ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldDescription[] order(FieldDescription[] persistentFields) {
|
||||||
|
UnloadedField[] unloadedFields = new UnloadedField[persistentFields.length];
|
||||||
|
for ( int i = 0; i < unloadedFields.length; i++ ) {
|
||||||
|
unloadedFields[i] = new UnloadedFieldDescription( persistentFields[i] );
|
||||||
|
}
|
||||||
|
UnloadedField[] ordered = enhancementContext.order( unloadedFields );
|
||||||
|
FieldDescription[] orderedFields = new FieldDescription[persistentFields.length];
|
||||||
|
for ( int i = 0; i < orderedFields.length; i++ ) {
|
||||||
|
orderedFields[i] = ( (UnloadedFieldDescription) ordered[i] ).fieldDescription;
|
||||||
|
}
|
||||||
|
return orderedFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLazyLoadable(FieldDescription field) {
|
||||||
|
return enhancementContext.isLazyLoadable( new UnloadedFieldDescription( field ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMappedCollection(FieldDescription field) {
|
||||||
|
return enhancementContext.isMappedCollection( new UnloadedFieldDescription( field ) );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,476 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
|
||||||
|
import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker;
|
||||||
|
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.EnhancerConstants;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
|
||||||
|
import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker;
|
||||||
|
import org.hibernate.engine.spi.CompositeOwner;
|
||||||
|
import org.hibernate.engine.spi.CompositeTracker;
|
||||||
|
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
class CodeTemplates {
|
||||||
|
|
||||||
|
static class SetOwner {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void $$_hibernate_setOwner(
|
||||||
|
@Advice.Argument(0) String name,
|
||||||
|
@Advice.Argument(1) CompositeOwner tracker,
|
||||||
|
@Advice.FieldValue(value = EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME, readOnly = false) CompositeOwnerTracker $$_hibernate_compositeOwners) {
|
||||||
|
if ( $$_hibernate_compositeOwners == null ) {
|
||||||
|
$$_hibernate_compositeOwners = new CompositeOwnerTracker();
|
||||||
|
}
|
||||||
|
$$_hibernate_compositeOwners.add( name, tracker );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ClearOwner {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void $$_hibernate_setOwner(
|
||||||
|
@Advice.Argument(0) String name,
|
||||||
|
@Advice.FieldValue(value = EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME, readOnly = false) CompositeOwnerTracker $$_hibernate_compositeOwners) {
|
||||||
|
if ( $$_hibernate_compositeOwners != null ) {
|
||||||
|
$$_hibernate_compositeOwners.removeOwner( name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TrackChange {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void $$_hibernate_trackChange(
|
||||||
|
@Advice.Argument(0) String name,
|
||||||
|
@Advice.FieldValue(value = EnhancerConstants.TRACKER_FIELD_NAME, readOnly = false) DirtyTracker $$_hibernate_tracker) {
|
||||||
|
if ( $$_hibernate_tracker == null ) {
|
||||||
|
$$_hibernate_tracker = new SimpleFieldTracker();
|
||||||
|
}
|
||||||
|
$$_hibernate_tracker.add( name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class GetDirtyAttributes {
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void $$_hibernate_getDirtyAttributes(
|
||||||
|
@Advice.This ExtendedSelfDirtinessTracker self,
|
||||||
|
@Advice.Return(readOnly = false) String[] returned,
|
||||||
|
@Advice.FieldValue(value = EnhancerConstants.TRACKER_FIELD_NAME, readOnly = false) DirtyTracker $$_hibernate_tracker,
|
||||||
|
@Advice.FieldValue(value = EnhancerConstants.TRACKER_COLLECTION_NAME, readOnly = false) CollectionTracker $$_hibernate_collectionTracker) {
|
||||||
|
if ( $$_hibernate_collectionTracker == null ) {
|
||||||
|
returned = ( $$_hibernate_tracker == null ) ? new String[0] : $$_hibernate_tracker.get();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( $$_hibernate_tracker == null ) {
|
||||||
|
$$_hibernate_tracker = new SimpleFieldTracker();
|
||||||
|
}
|
||||||
|
self.$$_hibernate_getCollectionFieldDirtyNames( $$_hibernate_tracker );
|
||||||
|
returned = $$_hibernate_tracker.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AreCollectionFieldsDirty {
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void $$_hibernate_hasDirtyAttributes(
|
||||||
|
@Advice.This ExtendedSelfDirtinessTracker self,
|
||||||
|
@Advice.Return(readOnly = false) boolean returned,
|
||||||
|
@Advice.FieldValue(value = EnhancerConstants.TRACKER_FIELD_NAME, readOnly = false) DirtyTracker $$_hibernate_tracker) {
|
||||||
|
returned = ( $$_hibernate_tracker != null && !$$_hibernate_tracker.isEmpty() ) || self.$$_hibernate_areCollectionFieldsDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ClearDirtyAttributes {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void $$_hibernate_clearDirtyAttributes(
|
||||||
|
@Advice.This ExtendedSelfDirtinessTracker self,
|
||||||
|
@Advice.FieldValue(value = EnhancerConstants.TRACKER_FIELD_NAME, readOnly = false) DirtyTracker $$_hibernate_tracker) {
|
||||||
|
if ( $$_hibernate_tracker != null ) {
|
||||||
|
$$_hibernate_tracker.clear();
|
||||||
|
}
|
||||||
|
self.$$_hibernate_clearDirtyCollectionNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SuspendDirtyTracking {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void $$_hibernate_suspendDirtyTracking(
|
||||||
|
@Advice.Argument(0) boolean suspend,
|
||||||
|
@Advice.FieldValue(value = EnhancerConstants.TRACKER_FIELD_NAME, readOnly = false) DirtyTracker $$_hibernate_tracker) {
|
||||||
|
if ( $$_hibernate_tracker == null ) {
|
||||||
|
$$_hibernate_tracker = new SimpleFieldTracker();
|
||||||
|
}
|
||||||
|
$$_hibernate_tracker.suspend( suspend );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CollectionAreCollectionFieldsDirty {
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void $$_hibernate_areCollectionFieldsDirty(
|
||||||
|
@Advice.Return(readOnly = false) boolean returned,
|
||||||
|
@FieldName String fieldName,
|
||||||
|
@FieldValue Collection<?> collection,
|
||||||
|
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
|
||||||
|
if ( !returned && $$_hibernate_collectionTracker != null ) {
|
||||||
|
if ( collection == null && $$_hibernate_collectionTracker.getSize( fieldName ) != -1 ) {
|
||||||
|
returned = true;
|
||||||
|
}
|
||||||
|
else if ( collection != null && $$_hibernate_collectionTracker.getSize( fieldName ) != collection.size() ) {
|
||||||
|
returned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MapAreCollectionFieldsDirty {
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void $$_hibernate_areCollectionFieldsDirty(
|
||||||
|
@Advice.Return(readOnly = false) boolean returned,
|
||||||
|
@FieldName String fieldName,
|
||||||
|
@FieldValue Map<?, ?> map,
|
||||||
|
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
|
||||||
|
if ( !returned && $$_hibernate_collectionTracker != null ) {
|
||||||
|
if ( map == null && $$_hibernate_collectionTracker.getSize( fieldName ) != -1 ) {
|
||||||
|
returned = true;
|
||||||
|
}
|
||||||
|
else if ( map != null && $$_hibernate_collectionTracker.getSize( fieldName ) != map.size() ) {
|
||||||
|
returned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CollectionGetCollectionFieldDirtyNames {
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void $$_hibernate_areCollectionFieldsDirty(
|
||||||
|
@FieldName String fieldName,
|
||||||
|
@FieldValue Collection<?> collection,
|
||||||
|
@Advice.Argument(0) DirtyTracker tracker,
|
||||||
|
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
|
||||||
|
if ( $$_hibernate_collectionTracker != null ) {
|
||||||
|
if ( collection == null && $$_hibernate_collectionTracker.getSize( fieldName ) != -1 ) {
|
||||||
|
tracker.add( fieldName );
|
||||||
|
}
|
||||||
|
else if ( collection != null && $$_hibernate_collectionTracker.getSize( fieldName ) != collection.size() ) {
|
||||||
|
tracker.add( fieldName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MapGetCollectionFieldDirtyNames {
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void $$_hibernate_areCollectionFieldsDirty(
|
||||||
|
@FieldName String fieldName,
|
||||||
|
@FieldValue Map<?, ?> map,
|
||||||
|
@Advice.Argument(0) DirtyTracker tracker,
|
||||||
|
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
|
||||||
|
if ( $$_hibernate_collectionTracker != null ) {
|
||||||
|
if ( map == null && $$_hibernate_collectionTracker.getSize( fieldName ) != -1 ) {
|
||||||
|
tracker.add( fieldName );
|
||||||
|
}
|
||||||
|
else if ( map != null && $$_hibernate_collectionTracker.getSize( fieldName ) != map.size() ) {
|
||||||
|
tracker.add( fieldName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CollectionGetCollectionClearDirtyNames {
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void $$_hibernate_clearDirtyCollectionNames(
|
||||||
|
@FieldName String fieldName,
|
||||||
|
@FieldValue Collection<?> collection,
|
||||||
|
@Advice.Argument(0) LazyAttributeLoadingInterceptor lazyInterceptor,
|
||||||
|
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
|
||||||
|
if ( lazyInterceptor == null || lazyInterceptor.isAttributeLoaded( fieldName ) ) {
|
||||||
|
if ( collection == null ) {
|
||||||
|
$$_hibernate_collectionTracker.add( fieldName, -1 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$$_hibernate_collectionTracker.add( fieldName, collection.size() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MapGetCollectionClearDirtyNames {
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void $$_hibernate_clearDirtyCollectionNames(
|
||||||
|
@FieldName String fieldName,
|
||||||
|
@FieldValue Map<?, ?> map,
|
||||||
|
@Advice.Argument(0) LazyAttributeLoadingInterceptor lazyInterceptor,
|
||||||
|
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) CollectionTracker $$_hibernate_collectionTracker) {
|
||||||
|
if ( lazyInterceptor == null || lazyInterceptor.isAttributeLoaded( fieldName ) ) {
|
||||||
|
if ( map == null ) {
|
||||||
|
$$_hibernate_collectionTracker.add( fieldName, -1 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$$_hibernate_collectionTracker.add( fieldName, map.size() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ClearDirtyCollectionNames {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void $$_hibernate_clearDirtyCollectionNames(
|
||||||
|
@Advice.This ExtendedSelfDirtinessTracker self,
|
||||||
|
@Advice.FieldValue(value = EnhancerConstants.TRACKER_COLLECTION_NAME, readOnly = false) CollectionTracker $$_hibernate_collectionTracker) {
|
||||||
|
if ( $$_hibernate_collectionTracker == null ) {
|
||||||
|
$$_hibernate_collectionTracker = new SimpleCollectionTracker();
|
||||||
|
}
|
||||||
|
self.$$_hibernate_removeDirtyFields( null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class InitializeLazyAttributeLoadingInterceptor {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void $$_hibernate_removeDirtyFields(
|
||||||
|
@Advice.Argument(value = 0, readOnly = false) LazyAttributeLoadingInterceptor lazyInterceptor,
|
||||||
|
@Advice.FieldValue(EnhancerConstants.TRACKER_COLLECTION_NAME) Object $$_hibernate_attributeInterceptor) {
|
||||||
|
if ( $$_hibernate_attributeInterceptor instanceof LazyAttributeLoadingInterceptor ) {
|
||||||
|
lazyInterceptor = (LazyAttributeLoadingInterceptor) $$_hibernate_attributeInterceptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CompositeFieldDirtyCheckingHandler {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void enter(@FieldName String fieldName, @FieldValue Object field) {
|
||||||
|
if ( field != null ) {
|
||||||
|
( (CompositeTracker) field ).$$_hibernate_clearOwner( fieldName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void exit(@Advice.This CompositeOwner self, @FieldName String fieldName, @FieldValue Object field) {
|
||||||
|
if ( field != null ) {
|
||||||
|
( (CompositeTracker) field ).$$_hibernate_setOwner( fieldName, self );
|
||||||
|
}
|
||||||
|
self.$$_hibernate_trackChange( fieldName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CompositeDirtyCheckingHandler {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void enter(@Advice.FieldValue(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME) CompositeOwnerTracker $$_hibernate_compositeOwners) {
|
||||||
|
if ( $$_hibernate_compositeOwners != null ) {
|
||||||
|
$$_hibernate_compositeOwners.callOwner( "" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CompositeOwnerDirtyCheckingHandler {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void $$_hibernate_trackChange(
|
||||||
|
@Advice.Argument(0) String name,
|
||||||
|
@Advice.FieldValue(EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME) CompositeOwnerTracker $$_hibernate_compositeOwners) {
|
||||||
|
if ( $$_hibernate_compositeOwners != null ) {
|
||||||
|
$$_hibernate_compositeOwners.callOwner( "." + name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class OneToOneHandler {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void enter(@FieldValue Object field, @Advice.Argument(0) Object argument, @MappedBy String mappedBy) {
|
||||||
|
if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) && argument != null ) {
|
||||||
|
setterNull( field, null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void exit(@Advice.This Object self, @Advice.Argument(0) Object argument, @MappedBy String mappedBy) {
|
||||||
|
if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) && getter( argument ) != self ) {
|
||||||
|
setterSelf( argument, self );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getter(Object target) {
|
||||||
|
// is replaced by the actual method call
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setterNull(Object target, Object argument) {
|
||||||
|
// is replaced by the actual method call
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setterSelf(Object target, Object argument) {
|
||||||
|
// is replaced by the actual method call
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class OneToManyOnCollectionHandler {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void enter(@FieldValue Collection<?> field, @Advice.Argument(0) Collection<?> argument, @MappedBy String mappedBy) {
|
||||||
|
if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) ) {
|
||||||
|
Object[] array = field.toArray();
|
||||||
|
for ( int i = 0; i < array.length; i++ ) {
|
||||||
|
if ( argument == null || !argument.contains( array[i] ) ) {
|
||||||
|
setterNull( array[i], null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void exit(@Advice.This Object self, @Advice.Argument(0) Collection<?> argument, @MappedBy String mappedBy) {
|
||||||
|
if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) ) {
|
||||||
|
Object[] array = argument.toArray();
|
||||||
|
for ( int i = 0; i < array.length; i++ ) {
|
||||||
|
if ( Hibernate.isPropertyInitialized( array[i], mappedBy ) && getter( array[i] ) != self ) {
|
||||||
|
setterSelf( array[i], self );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getter(Object target) {
|
||||||
|
// is replaced by the actual method call
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setterNull(Object target, Object argument) {
|
||||||
|
// is replaced by the actual method call
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setterSelf(Object target, Object argument) {
|
||||||
|
// is replaced by the actual method call
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class OneToManyOnMapHandler {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void enter(@FieldValue Map<?, ?> field, @Advice.Argument(0) Map<?, ?> argument, @MappedBy String mappedBy) {
|
||||||
|
if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) ) {
|
||||||
|
Object[] array = field.values().toArray();
|
||||||
|
for ( int i = 0; i < array.length; i++ ) {
|
||||||
|
if ( argument == null || !argument.values().contains( array[i] ) ) {
|
||||||
|
setterNull( array[i], null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void exit(@Advice.This Object self, @Advice.Argument(0) Map<?, ?> argument, @MappedBy String mappedBy) {
|
||||||
|
if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) ) {
|
||||||
|
Object[] array = argument.values().toArray();
|
||||||
|
for ( int i = 0; i < array.length; i++ ) {
|
||||||
|
if ( Hibernate.isPropertyInitialized( array[i], mappedBy ) && getter( array[i] ) != self ) {
|
||||||
|
setterSelf( array[i], self );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object getter(Object target) {
|
||||||
|
// is replaced with the actual getter call during instrumentation.
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setterNull(Object target, Object argument) {
|
||||||
|
// is replaced with the actual setter call during instrumentation.
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setterSelf(Object target, Object argument) {
|
||||||
|
// is replaced with the actual setter call during instrumentation.
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ManyToOneHandler {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void enter(@Advice.This Object self, @FieldValue Object field, @MappedBy String mappedBy) {
|
||||||
|
if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) ) {
|
||||||
|
Collection<?> c = getter( field );
|
||||||
|
if ( c != null ) {
|
||||||
|
c.remove( self );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void exit(@Advice.This Object self, @Advice.Argument(0) Object argument, @MappedBy String mappedBy) {
|
||||||
|
if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) ) {
|
||||||
|
Collection<Object> c = getter( argument );
|
||||||
|
if ( c != null && !c.contains( self ) ) {
|
||||||
|
c.add( self );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Collection<Object> getter(Object target) {
|
||||||
|
// is replaced by the actual method call
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ManyToManyHandler {
|
||||||
|
@Advice.OnMethodEnter
|
||||||
|
static void enter(@Advice.This Object self, @FieldValue Collection<?> field, @Advice.Argument(0) Collection<?> argument, @MappedBy String mappedBy) {
|
||||||
|
if ( field != null && Hibernate.isPropertyInitialized( field, mappedBy ) ) {
|
||||||
|
Object[] array = field.toArray();
|
||||||
|
for ( int i = 0; i < array.length; i++ ) {
|
||||||
|
if ( argument == null || !argument.contains( array[i] ) ) {
|
||||||
|
getter( array[i] ).remove( self );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit
|
||||||
|
static void exit(@Advice.This Object self, @Advice.Argument(0) Collection<?> argument, @MappedBy String mappedBy) {
|
||||||
|
if ( argument != null && Hibernate.isPropertyInitialized( argument, mappedBy ) ) {
|
||||||
|
Object[] array = argument.toArray();
|
||||||
|
for ( int i = 0; i < array.length; i++ ) {
|
||||||
|
if ( Hibernate.isPropertyInitialized( array[i], mappedBy ) ) {
|
||||||
|
Collection<Object> c = getter( array[i] );
|
||||||
|
if ( c != self && c != null ) {
|
||||||
|
c.add( self );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Collection<Object> getter(Object self) {
|
||||||
|
// is replaced by the actual method call
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface FieldName {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface FieldValue {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface MappedBy {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,443 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import javax.persistence.Access;
|
||||||
|
import javax.persistence.AccessType;
|
||||||
|
import javax.persistence.Transient;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
|
||||||
|
import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.CollectionTracker;
|
||||||
|
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.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
|
||||||
|
import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker;
|
||||||
|
import org.hibernate.engine.spi.CompositeOwner;
|
||||||
|
import org.hibernate.engine.spi.CompositeTracker;
|
||||||
|
import org.hibernate.engine.spi.EntityEntry;
|
||||||
|
import org.hibernate.engine.spi.Managed;
|
||||||
|
import org.hibernate.engine.spi.ManagedComposite;
|
||||||
|
import org.hibernate.engine.spi.ManagedEntity;
|
||||||
|
import org.hibernate.engine.spi.ManagedMappedSuperclass;
|
||||||
|
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||||
|
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||||
|
import org.hibernate.internal.CoreLogging;
|
||||||
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.annotation.AnnotationDescription;
|
||||||
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodList;
|
||||||
|
import net.bytebuddy.description.modifier.FieldManifestation;
|
||||||
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
|
import net.bytebuddy.description.type.TypeDefinition;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.ClassFileLocator;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import net.bytebuddy.dynamic.scaffold.MethodGraph;
|
||||||
|
import net.bytebuddy.dynamic.scaffold.TypeValidation;
|
||||||
|
import net.bytebuddy.implementation.FieldAccessor;
|
||||||
|
import net.bytebuddy.implementation.FixedValue;
|
||||||
|
import net.bytebuddy.implementation.Implementation;
|
||||||
|
import net.bytebuddy.implementation.StubMethod;
|
||||||
|
import net.bytebuddy.pool.TypePool;
|
||||||
|
import net.bytebuddy.utility.StreamDrainer;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isGetter;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
public class EnhancerImpl implements Enhancer {
|
||||||
|
|
||||||
|
private static final CoreMessageLogger log = CoreLogging.messageLogger( Enhancer.class );
|
||||||
|
|
||||||
|
protected final ByteBuddyEnhancementContext enhancementContext;
|
||||||
|
|
||||||
|
private final TypePool 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 ByteBuddyEnhancementContext( enhancementContext );
|
||||||
|
classPool = buildClassPool( this.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 {
|
||||||
|
try {
|
||||||
|
final TypeDescription managedCtClass = classPool.describe( className ).resolve();
|
||||||
|
DynamicType.Builder<?> builder = doEnhance(
|
||||||
|
new ByteBuddy().with( TypeValidation.DISABLED ).redefine( managedCtClass, ClassFileLocator.Simple.of( className, originalBytes ) ),
|
||||||
|
managedCtClass
|
||||||
|
);
|
||||||
|
if ( builder == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return builder.make().getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (RuntimeException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
log.unableToBuildEnhancementMetamodel( className );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] enhance(File javaClassFile) throws EnhancementException, IOException {
|
||||||
|
String name = javaClassFile.getName().substring( 0, javaClassFile.getName().length() - ".class".length() );
|
||||||
|
InputStream inputStream = new FileInputStream( javaClassFile );
|
||||||
|
try {
|
||||||
|
return enhance( name, StreamDrainer.DEFAULT.drain( inputStream ) );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypePool buildClassPool(final ByteBuddyEnhancementContext enhancementContext) {
|
||||||
|
return TypePool.Default.WithLazyResolution.of( enhancementContext.getLoadingClassLoader() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private DynamicType.Builder<?> doEnhance(DynamicType.Builder<?> builder, TypeDescription managedCtClass) {
|
||||||
|
// can't effectively enhance interfaces
|
||||||
|
if ( managedCtClass.isInterface() ) {
|
||||||
|
log.debugf( "Skipping enhancement of [%s]: it's an interface!", managedCtClass.getName() );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// skip already enhanced classes
|
||||||
|
if ( alreadyEnhanced( managedCtClass ) ) {
|
||||||
|
log.debugf( "Skipping enhancement of [%s]: already enhanced", managedCtClass.getName() );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistentAttributeTransformer transformer = PersistentAttributeTransformer.collectPersistentFields( managedCtClass, enhancementContext, classPool );
|
||||||
|
|
||||||
|
if ( enhancementContext.isEntityClass( managedCtClass ) ) {
|
||||||
|
log.infof( "Enhancing [%s] as Entity", managedCtClass.getName() );
|
||||||
|
builder = builder.implement( ManagedEntity.class )
|
||||||
|
.defineMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME, Object.class, Visibility.PUBLIC )
|
||||||
|
.intercept( FixedValue.self() );
|
||||||
|
|
||||||
|
builder = addFieldWithGetterAndSetter(
|
||||||
|
builder,
|
||||||
|
EntityEntry.class,
|
||||||
|
EnhancerConstants.ENTITY_ENTRY_FIELD_NAME,
|
||||||
|
EnhancerConstants.ENTITY_ENTRY_GETTER_NAME,
|
||||||
|
EnhancerConstants.ENTITY_ENTRY_SETTER_NAME
|
||||||
|
);
|
||||||
|
builder = addFieldWithGetterAndSetter(
|
||||||
|
builder,
|
||||||
|
ManagedEntity.class,
|
||||||
|
EnhancerConstants.PREVIOUS_FIELD_NAME,
|
||||||
|
EnhancerConstants.PREVIOUS_GETTER_NAME,
|
||||||
|
EnhancerConstants.PREVIOUS_SETTER_NAME
|
||||||
|
);
|
||||||
|
builder = addFieldWithGetterAndSetter(
|
||||||
|
builder,
|
||||||
|
ManagedEntity.class,
|
||||||
|
EnhancerConstants.NEXT_FIELD_NAME,
|
||||||
|
EnhancerConstants.NEXT_GETTER_NAME,
|
||||||
|
EnhancerConstants.NEXT_SETTER_NAME
|
||||||
|
);
|
||||||
|
|
||||||
|
builder = addInterceptorHandling( builder, managedCtClass );
|
||||||
|
|
||||||
|
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
|
||||||
|
builder = builder.implement( ExtendedSelfDirtinessTracker.class )
|
||||||
|
.defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldManifestation.TRANSIENT, Visibility.PRIVATE )
|
||||||
|
.annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() )
|
||||||
|
.defineField( EnhancerConstants.TRACKER_COLLECTION_NAME, CollectionTracker.class, FieldManifestation.TRANSIENT, Visibility.PRIVATE )
|
||||||
|
.annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() )
|
||||||
|
.defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC )
|
||||||
|
.withParameters( String.class )
|
||||||
|
.intercept( Advice.to( CodeTemplates.TrackChange.class ).wrap( StubMethod.INSTANCE ) )
|
||||||
|
.defineMethod( EnhancerConstants.TRACKER_GET_NAME, String[].class, Visibility.PUBLIC )
|
||||||
|
.intercept( Advice.to( CodeTemplates.GetDirtyAttributes.class ).wrap( StubMethod.INSTANCE ) )
|
||||||
|
.defineMethod( EnhancerConstants.TRACKER_HAS_CHANGED_NAME, boolean.class, Visibility.PUBLIC )
|
||||||
|
.intercept( Advice.to( CodeTemplates.AreCollectionFieldsDirty.class ).wrap( StubMethod.INSTANCE ) )
|
||||||
|
.defineMethod( EnhancerConstants.TRACKER_CLEAR_NAME, void.class, Visibility.PUBLIC )
|
||||||
|
.intercept( Advice.to( CodeTemplates.ClearDirtyAttributes.class ).wrap( StubMethod.INSTANCE ) )
|
||||||
|
.defineMethod( EnhancerConstants.TRACKER_SUSPEND_NAME, void.class, Visibility.PUBLIC )
|
||||||
|
.withParameters( boolean.class )
|
||||||
|
.intercept( Advice.to( CodeTemplates.SuspendDirtyTracking.class ).wrap( StubMethod.INSTANCE ) )
|
||||||
|
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC )
|
||||||
|
.intercept( FieldAccessor.ofField( EnhancerConstants.TRACKER_COLLECTION_NAME ) );
|
||||||
|
|
||||||
|
Implementation isDirty = StubMethod.INSTANCE, getDirtyNames = StubMethod.INSTANCE, clearDirtyNames = StubMethod.INSTANCE;
|
||||||
|
for ( FieldDescription collectionField : collectCollectionFields( managedCtClass ) ) {
|
||||||
|
if ( !enhancementContext.isMappedCollection( collectionField ) ) {
|
||||||
|
if ( collectionField.getType().asErasure().isAssignableTo( Map.class ) ) {
|
||||||
|
isDirty = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
|
||||||
|
.bind( CodeTemplates.FieldValue.class, collectionField )
|
||||||
|
.to( CodeTemplates.MapAreCollectionFieldsDirty.class )
|
||||||
|
.wrap( isDirty );
|
||||||
|
getDirtyNames = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
|
||||||
|
.bind( CodeTemplates.FieldValue.class, collectionField )
|
||||||
|
.to( CodeTemplates.MapGetCollectionFieldDirtyNames.class )
|
||||||
|
.wrap( getDirtyNames );
|
||||||
|
clearDirtyNames = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
|
||||||
|
.bind( CodeTemplates.FieldValue.class, collectionField )
|
||||||
|
.to( CodeTemplates.MapGetCollectionClearDirtyNames.class )
|
||||||
|
.wrap( clearDirtyNames );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isDirty = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
|
||||||
|
.bind( CodeTemplates.FieldValue.class, collectionField )
|
||||||
|
.to( CodeTemplates.CollectionAreCollectionFieldsDirty.class )
|
||||||
|
.wrap( isDirty );
|
||||||
|
getDirtyNames = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
|
||||||
|
.bind( CodeTemplates.FieldValue.class, collectionField )
|
||||||
|
.to( CodeTemplates.CollectionGetCollectionFieldDirtyNames.class )
|
||||||
|
.wrap( getDirtyNames );
|
||||||
|
clearDirtyNames = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldName.class, collectionField.getName() )
|
||||||
|
.bind( CodeTemplates.FieldValue.class, collectionField )
|
||||||
|
.to( CodeTemplates.CollectionGetCollectionClearDirtyNames.class )
|
||||||
|
.wrap( clearDirtyNames );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) {
|
||||||
|
clearDirtyNames = Advice.to( CodeTemplates.InitializeLazyAttributeLoadingInterceptor.class ).wrap( clearDirtyNames );
|
||||||
|
}
|
||||||
|
|
||||||
|
builder = builder.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME, boolean.class, Visibility.PUBLIC )
|
||||||
|
.intercept( isDirty )
|
||||||
|
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME, void.class, Visibility.PUBLIC )
|
||||||
|
.withParameters( DirtyTracker.class )
|
||||||
|
.intercept( getDirtyNames )
|
||||||
|
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME, void.class, Visibility.PUBLIC )
|
||||||
|
.intercept( Advice.withCustomMapping().to( CodeTemplates.ClearDirtyCollectionNames.class ).wrap( StubMethod.INSTANCE ) )
|
||||||
|
.defineMethod( ExtendedSelfDirtinessTracker.REMOVE_DIRTY_FIELDS_NAME, void.class, Visibility.PUBLIC )
|
||||||
|
.withParameters( LazyAttributeLoadingInterceptor.class )
|
||||||
|
.intercept( clearDirtyNames );
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformer.applyTo( builder, false );
|
||||||
|
}
|
||||||
|
else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
|
||||||
|
log.infof( "Enhancing [%s] as Composite", managedCtClass.getName() );
|
||||||
|
|
||||||
|
builder = builder.implement( ManagedComposite.class );
|
||||||
|
builder = addInterceptorHandling( builder, managedCtClass );
|
||||||
|
|
||||||
|
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
|
||||||
|
builder = builder.implement( CompositeTracker.class )
|
||||||
|
.defineField(
|
||||||
|
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME,
|
||||||
|
CompositeOwnerTracker.class,
|
||||||
|
FieldManifestation.TRANSIENT,
|
||||||
|
Visibility.PRIVATE
|
||||||
|
)
|
||||||
|
.annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() )
|
||||||
|
.defineMethod(
|
||||||
|
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
|
||||||
|
void.class,
|
||||||
|
Visibility.PUBLIC
|
||||||
|
)
|
||||||
|
.withParameters( String.class, CompositeOwner.class )
|
||||||
|
.intercept( Advice.to( CodeTemplates.SetOwner.class ).wrap( StubMethod.INSTANCE ) )
|
||||||
|
.defineMethod(
|
||||||
|
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER,
|
||||||
|
void.class,
|
||||||
|
Visibility.PUBLIC
|
||||||
|
)
|
||||||
|
.withParameters( String.class )
|
||||||
|
.intercept( Advice.to( CodeTemplates.ClearOwner.class ).wrap( StubMethod.INSTANCE ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformer.applyTo( builder, false );
|
||||||
|
}
|
||||||
|
else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
|
||||||
|
log.infof( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() );
|
||||||
|
return transformer.applyTo( builder, true );
|
||||||
|
}
|
||||||
|
else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
|
||||||
|
log.infof( "Extended enhancement of [%s]", managedCtClass.getName() );
|
||||||
|
return transformer.applyExtended( builder );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.debugf( "Skipping enhancement of [%s]: not entity or composite", managedCtClass.getName() );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean alreadyEnhanced(TypeDescription managedCtClass) {
|
||||||
|
if ( !managedCtClass.isAssignableTo( Managed.class ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// HHH-10977 - When a mapped superclass gets enhanced before a subclassing entity, the entity does not get enhanced, but it implements the Managed interface
|
||||||
|
return enhancementContext.isEntityClass( managedCtClass ) && managedCtClass.isAssignableTo( ManagedEntity.class )
|
||||||
|
|| enhancementContext.isCompositeClass( managedCtClass ) && managedCtClass.isAssignableTo( ManagedComposite.class )
|
||||||
|
|| enhancementContext.isMappedSuperclassClass( managedCtClass ) && managedCtClass.isAssignableTo( ManagedMappedSuperclass.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
private DynamicType.Builder<?> addInterceptorHandling(DynamicType.Builder<?> builder, TypeDescription managedCtClass) {
|
||||||
|
// interceptor handling is only needed if class has lazy-loadable attributes
|
||||||
|
if ( enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) {
|
||||||
|
log.debugf( "Weaving in PersistentAttributeInterceptable implementation on [%s]", managedCtClass.getName() );
|
||||||
|
|
||||||
|
builder = builder.implement( PersistentAttributeInterceptable.class );
|
||||||
|
|
||||||
|
builder = addFieldWithGetterAndSetter(
|
||||||
|
builder,
|
||||||
|
PersistentAttributeInterceptor.class,
|
||||||
|
EnhancerConstants.INTERCEPTOR_FIELD_NAME,
|
||||||
|
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
|
||||||
|
EnhancerConstants.INTERCEPTOR_SETTER_NAME
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DynamicType.Builder<?> addFieldWithGetterAndSetter(
|
||||||
|
DynamicType.Builder<?> builder,
|
||||||
|
Class<?> type,
|
||||||
|
String fieldName,
|
||||||
|
String getterName,
|
||||||
|
String setterName) {
|
||||||
|
return builder.defineField( fieldName, type, Visibility.PRIVATE, FieldManifestation.TRANSIENT )
|
||||||
|
.annotateField( AnnotationDescription.Builder.ofType( Transient.class ).build() )
|
||||||
|
.defineMethod( getterName, type, Visibility.PUBLIC )
|
||||||
|
.intercept( FieldAccessor.ofField( fieldName ) )
|
||||||
|
.defineMethod( setterName, void.class, Visibility.PUBLIC )
|
||||||
|
.withParameters( type )
|
||||||
|
.intercept( FieldAccessor.ofField( fieldName ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<FieldDescription> collectCollectionFields(TypeDescription managedCtClass) {
|
||||||
|
List<FieldDescription> collectionList = new ArrayList<>();
|
||||||
|
|
||||||
|
for ( FieldDescription 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 ) ) {
|
||||||
|
if ( ctField.getType().asErasure().isAssignableTo( Collection.class ) || ctField.getType().asErasure().isAssignableTo( Map.class ) ) {
|
||||||
|
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<FieldDescription> collectInheritCollectionFields(TypeDefinition managedCtClass) {
|
||||||
|
TypeDefinition managedCtSuperclass = managedCtClass.getSuperClass();
|
||||||
|
if ( managedCtSuperclass == null || managedCtSuperclass.represents( Object.class ) ) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !enhancementContext.isMappedSuperclassClass( managedCtSuperclass.asErasure() ) ) {
|
||||||
|
return collectInheritCollectionFields( managedCtSuperclass.asErasure() );
|
||||||
|
}
|
||||||
|
List<FieldDescription> collectionList = new ArrayList<FieldDescription>();
|
||||||
|
|
||||||
|
for ( FieldDescription ctField : managedCtSuperclass.getDeclaredFields() ) {
|
||||||
|
if ( !Modifier.isStatic( ctField.getModifiers() ) && enhancementContext.isPersistentField( ctField ) ) {
|
||||||
|
if ( ctField.getType().asErasure().isAssignableTo( Collection.class ) || ctField.getType().asErasure().isAssignableTo( Map.class ) ) {
|
||||||
|
collectionList.add( ctField );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collectionList.addAll( collectInheritCollectionFields( managedCtSuperclass ) );
|
||||||
|
return collectionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String capitalize(String value) {
|
||||||
|
return Character.toUpperCase( value.charAt( 0 ) ) + value.substring( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isAnnotationPresent(FieldDescription fieldDescription, Class<? extends Annotation> type) {
|
||||||
|
return getAnnotation( fieldDescription, type ) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T extends Annotation> AnnotationDescription.Loadable<T> getAnnotation(FieldDescription fieldDescription, Class<T> type) {
|
||||||
|
AnnotationDescription.Loadable<Access> access = fieldDescription.getDeclaringType().asErasure().getDeclaredAnnotations().ofType( Access.class );
|
||||||
|
if ( access != null && access.loadSilent().value() == AccessType.PROPERTY ) {
|
||||||
|
MethodDescription getter = getterOf( fieldDescription );
|
||||||
|
if ( getter == null ) {
|
||||||
|
return fieldDescription.getDeclaredAnnotations().ofType( type );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return getter.getDeclaredAnnotations().ofType( type );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( access != null && access.loadSilent().value() == AccessType.FIELD ) {
|
||||||
|
return fieldDescription.getDeclaredAnnotations().ofType( type );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MethodDescription getter = getterOf( fieldDescription );
|
||||||
|
if ( getter != null ) {
|
||||||
|
AnnotationDescription.Loadable<T> annotationDescription = getter.getDeclaredAnnotations().ofType( type );
|
||||||
|
if ( annotationDescription != null ) {
|
||||||
|
return annotationDescription;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fieldDescription.getDeclaredAnnotations().ofType( type );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MethodDescription getterOf(FieldDescription persistentField) {
|
||||||
|
MethodList<?> methodList = MethodGraph.Compiler.DEFAULT.compile( persistentField.getDeclaringType().asErasure() )
|
||||||
|
.listNodes()
|
||||||
|
.asMethodList()
|
||||||
|
.filter( isGetter(persistentField.getName() ) );
|
||||||
|
if ( methodList.size() == 1 ) {
|
||||||
|
return methodList.getOnly();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancementException;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
|
import org.hibernate.internal.CoreLogging;
|
||||||
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
|
||||||
|
import net.bytebuddy.ClassFileVersion;
|
||||||
|
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||||
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
|
import net.bytebuddy.description.field.FieldList;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.implementation.Implementation;
|
||||||
|
import net.bytebuddy.jar.asm.MethodVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import net.bytebuddy.jar.asm.Type;
|
||||||
|
import net.bytebuddy.pool.TypePool;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.hasDescriptor;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
|
class FieldAccessEnhancer implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper {
|
||||||
|
|
||||||
|
private static final CoreMessageLogger log = CoreLogging.messageLogger( FieldAccessEnhancer.class );
|
||||||
|
|
||||||
|
private final TypeDescription managedCtClass;
|
||||||
|
|
||||||
|
private final ByteBuddyEnhancementContext enhancementContext;
|
||||||
|
|
||||||
|
private final TypePool classPool;
|
||||||
|
|
||||||
|
FieldAccessEnhancer(TypeDescription managedCtClass, ByteBuddyEnhancementContext enhancementContext, TypePool classPool) {
|
||||||
|
this.managedCtClass = managedCtClass;
|
||||||
|
this.enhancementContext = enhancementContext;
|
||||||
|
this.classPool = classPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodVisitor wrap(
|
||||||
|
TypeDescription instrumentedType,
|
||||||
|
MethodDescription.InDefinedShape instrumentedMethod,
|
||||||
|
MethodVisitor methodVisitor,
|
||||||
|
Implementation.Context implementationContext,
|
||||||
|
TypePool typePool,
|
||||||
|
int writerFlags,
|
||||||
|
int readerFlags) {
|
||||||
|
return new MethodVisitor( Opcodes.ASM5, methodVisitor ) {
|
||||||
|
@Override
|
||||||
|
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||||
|
if ( opcode != Opcodes.GETFIELD && opcode != Opcodes.PUTFIELD ) {
|
||||||
|
super.visitFieldInsn( opcode, owner, name, desc );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldDescription field = findField( owner, name, desc );
|
||||||
|
|
||||||
|
if ( ( enhancementContext.isEntityClass( field.getDeclaringType().asErasure() )
|
||||||
|
|| enhancementContext.isCompositeClass( field.getDeclaringType().asErasure() ) )
|
||||||
|
&& !field.getType().asErasure().equals( managedCtClass )
|
||||||
|
&& enhancementContext.isPersistentField( field )
|
||||||
|
&& !EnhancerImpl.isAnnotationPresent( field, Id.class )
|
||||||
|
&& !field.getName().equals( "this$0" ) ) {
|
||||||
|
|
||||||
|
log.debugf(
|
||||||
|
"Extended enhancement: Transforming access to field [%s.%s] from method [%s#%s]",
|
||||||
|
field.getType().asErasure(),
|
||||||
|
field.getName(),
|
||||||
|
field.getName(),
|
||||||
|
name
|
||||||
|
);
|
||||||
|
|
||||||
|
switch ( opcode ) {
|
||||||
|
case Opcodes.GETFIELD:
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
owner,
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + name,
|
||||||
|
Type.getMethodDescriptor( Type.getType( desc ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
case Opcodes.PUTFIELD:
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
owner,
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + name,
|
||||||
|
Type.getMethodDescriptor( Type.getType( void.class ), Type.getType( desc ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
throw new EnhancementException( "Unexpected opcode: " + opcode );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitFieldInsn( opcode, owner, name, desc );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private FieldDescription findField(String owner, String name, String desc) {
|
||||||
|
TypePool.Resolution resolution = classPool.describe( owner.replace( '/', '.' ) );
|
||||||
|
if ( !resolution.isResolved() ) {
|
||||||
|
final String msg = String.format(
|
||||||
|
"Unable to perform extended enhancement - Unable to locate [%s]",
|
||||||
|
owner.replace( '/', '.' )
|
||||||
|
);
|
||||||
|
throw new EnhancementException( msg );
|
||||||
|
}
|
||||||
|
FieldList<?> fields = resolution.resolve().getDeclaredFields().filter( named( name ).and( hasDescriptor( desc ) ) );
|
||||||
|
if ( fields.size() != 1 ) {
|
||||||
|
final String msg = String.format(
|
||||||
|
"Unable to perform extended enhancement - No unique field [%s] defined by [%s]",
|
||||||
|
name,
|
||||||
|
owner.replace( '/', '.' )
|
||||||
|
);
|
||||||
|
throw new EnhancementException( msg );
|
||||||
|
}
|
||||||
|
return fields.getOnly();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
|
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||||
|
|
||||||
|
import net.bytebuddy.ClassFileVersion;
|
||||||
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.implementation.Implementation;
|
||||||
|
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
|
||||||
|
import net.bytebuddy.jar.asm.Label;
|
||||||
|
import net.bytebuddy.jar.asm.MethodVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import net.bytebuddy.jar.asm.Type;
|
||||||
|
|
||||||
|
abstract class FieldReaderAppender implements ByteCodeAppender {
|
||||||
|
|
||||||
|
protected final TypeDescription managedCtClass;
|
||||||
|
|
||||||
|
protected final FieldDescription persistentField;
|
||||||
|
|
||||||
|
private FieldReaderAppender(TypeDescription managedCtClass, FieldDescription.InDefinedShape persistentField) {
|
||||||
|
this.managedCtClass = managedCtClass;
|
||||||
|
this.persistentField = persistentField;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ByteCodeAppender of(TypeDescription managedCtClass, FieldDescription persistentField) {
|
||||||
|
if ( !persistentField.isVisibleTo( managedCtClass ) ) {
|
||||||
|
return new MethodDispatching( managedCtClass, persistentField.asDefined() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new FieldWriting( managedCtClass, persistentField.asDefined() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size apply(
|
||||||
|
MethodVisitor methodVisitor,
|
||||||
|
Implementation.Context implementationContext,
|
||||||
|
MethodDescription instrumentedMethod) {
|
||||||
|
TypeDescription dispatcherType = persistentField.getType().isPrimitive()
|
||||||
|
? persistentField.getType().asErasure()
|
||||||
|
: TypeDescription.OBJECT;
|
||||||
|
// if ( this.$$_hibernate_getInterceptor() != null )
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
managedCtClass.getInternalName(),
|
||||||
|
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
|
||||||
|
Type.getMethodDescriptor( Type.getType( PersistentAttributeInterceptor.class ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
Label skip = new Label();
|
||||||
|
methodVisitor.visitJumpInsn( Opcodes.IFNULL, skip );
|
||||||
|
// this (for field write)
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
// this.$$_hibernate_getInterceptor();
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
managedCtClass.getInternalName(),
|
||||||
|
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
|
||||||
|
Type.getMethodDescriptor( Type.getType( PersistentAttributeInterceptor.class ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
// .readXXX( self, fieldName, field );
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
methodVisitor.visitLdcInsn( persistentField.getName() );
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
fieldRead( methodVisitor );
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEINTERFACE,
|
||||||
|
Type.getInternalName( PersistentAttributeInterceptor.class ),
|
||||||
|
"read" + EnhancerImpl.capitalize( dispatcherType.getSimpleName() ),
|
||||||
|
Type.getMethodDescriptor(
|
||||||
|
Type.getType( dispatcherType.getDescriptor() ),
|
||||||
|
Type.getType( Object.class ),
|
||||||
|
Type.getType( String.class ),
|
||||||
|
Type.getType( dispatcherType.getDescriptor() )
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
// field = (cast) XXX
|
||||||
|
if ( !dispatcherType.isPrimitive() ) {
|
||||||
|
methodVisitor.visitTypeInsn( Opcodes.CHECKCAST, persistentField.getType().asErasure().getInternalName() );
|
||||||
|
}
|
||||||
|
fieldWrite( methodVisitor );
|
||||||
|
// end if
|
||||||
|
methodVisitor.visitLabel( skip );
|
||||||
|
if ( implementationContext.getClassFileVersion().isAtLeast( ClassFileVersion.JAVA_V6 ) ) {
|
||||||
|
methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null );
|
||||||
|
}
|
||||||
|
// return field
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
fieldRead( methodVisitor );
|
||||||
|
methodVisitor.visitInsn( Type.getType( persistentField.getType().asErasure().getDescriptor() ).getOpcode( Opcodes.IRETURN ) );
|
||||||
|
return new Size( 4 + persistentField.getType().getStackSize().getSize(), instrumentedMethod.getStackSize() );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void fieldRead(MethodVisitor methodVisitor);
|
||||||
|
|
||||||
|
protected abstract void fieldWrite(MethodVisitor methodVisitor);
|
||||||
|
|
||||||
|
private static class FieldWriting extends FieldReaderAppender {
|
||||||
|
|
||||||
|
private FieldWriting(TypeDescription managedCtClass, FieldDescription.InDefinedShape fieldDescription) {
|
||||||
|
super( managedCtClass, fieldDescription );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void fieldRead(MethodVisitor methodVisitor) {
|
||||||
|
methodVisitor.visitFieldInsn(
|
||||||
|
Opcodes.GETFIELD,
|
||||||
|
persistentField.getDeclaringType().asErasure().getInternalName(),
|
||||||
|
persistentField.getInternalName(),
|
||||||
|
persistentField.getDescriptor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void fieldWrite(MethodVisitor methodVisitor) {
|
||||||
|
methodVisitor.visitFieldInsn(
|
||||||
|
Opcodes.PUTFIELD,
|
||||||
|
persistentField.getDeclaringType().asErasure().getInternalName(),
|
||||||
|
persistentField.getInternalName(),
|
||||||
|
persistentField.getDescriptor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MethodDispatching extends FieldReaderAppender {
|
||||||
|
|
||||||
|
private MethodDispatching(TypeDescription managedCtClass, FieldDescription.InDefinedShape fieldDescription) {
|
||||||
|
super( managedCtClass, fieldDescription );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void fieldRead(MethodVisitor methodVisitor) {
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESPECIAL,
|
||||||
|
managedCtClass.getSuperClass().asErasure().getInternalName(),
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + persistentField.getName(),
|
||||||
|
Type.getMethodDescriptor( Type.getType( persistentField.getType().asErasure().getDescriptor() ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void fieldWrite(MethodVisitor methodVisitor) {
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESPECIAL,
|
||||||
|
managedCtClass.getSuperClass().asErasure().getInternalName(),
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + persistentField.getName(),
|
||||||
|
Type.getMethodDescriptor( Type.getType( void.class ), Type.getType( persistentField.getType().asErasure().getDescriptor() ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
|
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||||
|
|
||||||
|
import net.bytebuddy.ClassFileVersion;
|
||||||
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.implementation.Implementation;
|
||||||
|
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
|
||||||
|
import net.bytebuddy.jar.asm.Label;
|
||||||
|
import net.bytebuddy.jar.asm.MethodVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import net.bytebuddy.jar.asm.Type;
|
||||||
|
|
||||||
|
abstract class FieldWriterAppender implements ByteCodeAppender {
|
||||||
|
|
||||||
|
protected final TypeDescription managedCtClass;
|
||||||
|
|
||||||
|
protected final FieldDescription.InDefinedShape persistentField;
|
||||||
|
|
||||||
|
private FieldWriterAppender(TypeDescription managedCtClass, FieldDescription.InDefinedShape persistentField) {
|
||||||
|
this.managedCtClass = managedCtClass;
|
||||||
|
this.persistentField = persistentField;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ByteCodeAppender of(TypeDescription managedCtClass, FieldDescription persistentField) {
|
||||||
|
if ( !persistentField.isVisibleTo( managedCtClass ) ) {
|
||||||
|
return new MethodDispatching( managedCtClass, persistentField.asDefined() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new FieldWriting( managedCtClass, persistentField.asDefined() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size apply(
|
||||||
|
MethodVisitor methodVisitor,
|
||||||
|
Implementation.Context implementationContext,
|
||||||
|
MethodDescription instrumentedMethod) {
|
||||||
|
TypeDescription dispatcherType = persistentField.getType().isPrimitive()
|
||||||
|
? persistentField.getType().asErasure()
|
||||||
|
: TypeDescription.OBJECT;
|
||||||
|
// if ( this.$$_hibernate_getInterceptor() != null )
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
managedCtClass.getInternalName(),
|
||||||
|
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
|
||||||
|
Type.getMethodDescriptor( Type.getType( PersistentAttributeInterceptor.class ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
Label noInterceptor = new Label();
|
||||||
|
methodVisitor.visitJumpInsn( Opcodes.IFNULL, noInterceptor );
|
||||||
|
// this (for field write)
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
// this.$$_hibernate_getInterceptor();
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
managedCtClass.getInternalName(),
|
||||||
|
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
|
||||||
|
Type.getMethodDescriptor( Type.getType( PersistentAttributeInterceptor.class ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
// .writeXXX( self, fieldName, field, arg1 );
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
methodVisitor.visitLdcInsn( persistentField.getName() );
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
fieldRead( methodVisitor );
|
||||||
|
methodVisitor.visitVarInsn( Type.getType( dispatcherType.getDescriptor() ).getOpcode( Opcodes.ILOAD ), 1 );
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEINTERFACE,
|
||||||
|
Type.getInternalName( PersistentAttributeInterceptor.class ),
|
||||||
|
"write" + EnhancerImpl.capitalize( dispatcherType.getSimpleName() ),
|
||||||
|
Type.getMethodDescriptor(
|
||||||
|
Type.getType( dispatcherType.getDescriptor() ),
|
||||||
|
Type.getType( Object.class ),
|
||||||
|
Type.getType( String.class ),
|
||||||
|
Type.getType( dispatcherType.getDescriptor() ),
|
||||||
|
Type.getType( dispatcherType.getDescriptor() )
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
// arg1 = (cast) XXX
|
||||||
|
if ( !dispatcherType.isPrimitive() ) {
|
||||||
|
methodVisitor.visitTypeInsn( Opcodes.CHECKCAST, persistentField.getType().asErasure().getInternalName() );
|
||||||
|
}
|
||||||
|
fieldWrite( methodVisitor );
|
||||||
|
// return
|
||||||
|
methodVisitor.visitInsn( Opcodes.RETURN );
|
||||||
|
// else
|
||||||
|
methodVisitor.visitLabel( noInterceptor );
|
||||||
|
if ( implementationContext.getClassFileVersion().isAtLeast( ClassFileVersion.JAVA_V6 ) ) {
|
||||||
|
methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null );
|
||||||
|
}
|
||||||
|
// this (for field write)
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
// arg1 = (cast) XXX
|
||||||
|
methodVisitor.visitVarInsn( Type.getType( dispatcherType.getDescriptor() ).getOpcode( Opcodes.ILOAD ), 1 );
|
||||||
|
if ( !dispatcherType.isPrimitive() ) {
|
||||||
|
methodVisitor.visitTypeInsn( Opcodes.CHECKCAST, persistentField.getType().asErasure().getInternalName() );
|
||||||
|
}
|
||||||
|
fieldWrite( methodVisitor );
|
||||||
|
// return
|
||||||
|
methodVisitor.visitInsn( Opcodes.RETURN );
|
||||||
|
return new Size( 4 + 2 * persistentField.getType().getStackSize().getSize(), instrumentedMethod.getStackSize() );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void fieldRead(MethodVisitor methodVisitor);
|
||||||
|
|
||||||
|
protected abstract void fieldWrite(MethodVisitor methodVisitor);
|
||||||
|
|
||||||
|
private static class FieldWriting extends FieldWriterAppender {
|
||||||
|
|
||||||
|
private FieldWriting(TypeDescription managedCtClass, FieldDescription.InDefinedShape fieldDescription) {
|
||||||
|
super( managedCtClass, fieldDescription );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void fieldRead(MethodVisitor methodVisitor) {
|
||||||
|
methodVisitor.visitFieldInsn(
|
||||||
|
Opcodes.GETFIELD,
|
||||||
|
persistentField.getDeclaringType().asErasure().getInternalName(),
|
||||||
|
persistentField.getInternalName(),
|
||||||
|
persistentField.getDescriptor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void fieldWrite(MethodVisitor methodVisitor) {
|
||||||
|
methodVisitor.visitFieldInsn(
|
||||||
|
Opcodes.PUTFIELD,
|
||||||
|
persistentField.getDeclaringType().asErasure().getInternalName(),
|
||||||
|
persistentField.getInternalName(),
|
||||||
|
persistentField.getDescriptor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MethodDispatching extends FieldWriterAppender {
|
||||||
|
|
||||||
|
private MethodDispatching(TypeDescription managedCtClass, FieldDescription.InDefinedShape fieldDescription) {
|
||||||
|
super( managedCtClass, fieldDescription );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void fieldRead(MethodVisitor methodVisitor) {
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESPECIAL,
|
||||||
|
managedCtClass.getSuperClass().asErasure().getInternalName(),
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + persistentField.getName(),
|
||||||
|
Type.getMethodDescriptor( Type.getType( persistentField.getType().asErasure().getDescriptor() ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void fieldWrite(MethodVisitor methodVisitor) {
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESPECIAL,
|
||||||
|
managedCtClass.getSuperClass().asErasure().getInternalName(),
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + persistentField.getName(),
|
||||||
|
Type.getMethodDescriptor( Type.getType( void.class ), Type.getType( persistentField.getType().asErasure().getDescriptor() ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import javax.persistence.Embedded;
|
||||||
|
import javax.persistence.EmbeddedId;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
|
import org.hibernate.internal.util.compare.EqualsHelper;
|
||||||
|
|
||||||
|
import net.bytebuddy.ClassFileVersion;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
|
||||||
|
import net.bytebuddy.implementation.Implementation;
|
||||||
|
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
|
||||||
|
import net.bytebuddy.jar.asm.Label;
|
||||||
|
import net.bytebuddy.jar.asm.MethodVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import net.bytebuddy.jar.asm.Type;
|
||||||
|
|
||||||
|
class InlineDirtyCheckingHandler implements Implementation, ByteCodeAppender {
|
||||||
|
|
||||||
|
private final Implementation delegate;
|
||||||
|
|
||||||
|
private final TypeDescription managedCtClass;
|
||||||
|
|
||||||
|
private final FieldDescription.InDefinedShape persistentField;
|
||||||
|
|
||||||
|
private InlineDirtyCheckingHandler(Implementation delegate, TypeDescription managedCtClass, FieldDescription.InDefinedShape persistentField) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.managedCtClass = managedCtClass;
|
||||||
|
this.persistentField = persistentField;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Implementation wrap(
|
||||||
|
TypeDescription managedCtClass,
|
||||||
|
ByteBuddyEnhancementContext enhancementContext,
|
||||||
|
FieldDescription persistentField,
|
||||||
|
Implementation implementation) {
|
||||||
|
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
|
||||||
|
|
||||||
|
if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
|
||||||
|
implementation = Advice.to( CodeTemplates.CompositeDirtyCheckingHandler.class ).wrap( implementation );
|
||||||
|
}
|
||||||
|
else if ( !EnhancerImpl.isAnnotationPresent( persistentField, Id.class )
|
||||||
|
&& !EnhancerImpl.isAnnotationPresent( persistentField, EmbeddedId.class )
|
||||||
|
&& !( persistentField.getType().asErasure().isAssignableTo( Collection.class )
|
||||||
|
&& enhancementContext.isMappedCollection( persistentField ) ) ) {
|
||||||
|
implementation = new InlineDirtyCheckingHandler( implementation, managedCtClass, persistentField.asDefined() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( enhancementContext.isCompositeClass( persistentField.getType().asErasure() )
|
||||||
|
&& EnhancerImpl.isAnnotationPresent( persistentField, Embedded.class ) ) {
|
||||||
|
|
||||||
|
implementation = Advice.withCustomMapping()
|
||||||
|
.bind( CodeTemplates.FieldValue.class, persistentField )
|
||||||
|
.bind( CodeTemplates.FieldName.class, persistentField.getName() )
|
||||||
|
.to( CodeTemplates.CompositeFieldDirtyCheckingHandler.class )
|
||||||
|
.wrap( implementation );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implementation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteCodeAppender appender(Target implementationTarget) {
|
||||||
|
return new ByteCodeAppender.Compound( this, delegate.appender( implementationTarget ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstrumentedType prepare(InstrumentedType instrumentedType) {
|
||||||
|
return delegate.prepare( instrumentedType );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size apply(
|
||||||
|
MethodVisitor methodVisitor,
|
||||||
|
Context implementationContext,
|
||||||
|
MethodDescription instrumentedMethod) {
|
||||||
|
// if (arg != field) {
|
||||||
|
methodVisitor.visitVarInsn( Type.getType( persistentField.getType().asErasure().getDescriptor() ).getOpcode( Opcodes.ILOAD ), 1 );
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
if ( persistentField.getDeclaringType().asErasure().equals( managedCtClass ) ) {
|
||||||
|
methodVisitor.visitFieldInsn(
|
||||||
|
Opcodes.GETFIELD,
|
||||||
|
persistentField.getDeclaringType().asErasure().getInternalName(),
|
||||||
|
persistentField.getName(),
|
||||||
|
persistentField.getDescriptor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
persistentField.getDeclaringType().asErasure().getInternalName(),
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + persistentField.getName(),
|
||||||
|
Type.getMethodDescriptor( Type.getType( persistentField.getDescriptor() ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
int branchCode;
|
||||||
|
if ( persistentField.getType().isPrimitive() ) {
|
||||||
|
if ( persistentField.getType().represents( long.class ) ) {
|
||||||
|
methodVisitor.visitInsn( Opcodes.LCMP );
|
||||||
|
}
|
||||||
|
else if ( persistentField.getType().represents( float.class ) ) {
|
||||||
|
methodVisitor.visitInsn( Opcodes.FCMPL );
|
||||||
|
}
|
||||||
|
else if ( persistentField.getType().represents( double.class ) ) {
|
||||||
|
methodVisitor.visitInsn( Opcodes.DCMPL );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
methodVisitor.visitInsn( Opcodes.ISUB );
|
||||||
|
}
|
||||||
|
branchCode = Opcodes.IFEQ;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESTATIC,
|
||||||
|
Type.getInternalName( EqualsHelper.class ),
|
||||||
|
"areEqual",
|
||||||
|
Type.getMethodDescriptor( Type.getType( boolean.class ), Type.getType( Object.class ), Type.getType( Object.class ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
branchCode = Opcodes.IFNE;
|
||||||
|
}
|
||||||
|
Label skip = new Label();
|
||||||
|
methodVisitor.visitJumpInsn( branchCode, skip );
|
||||||
|
// this.$$_hibernate_trackChange(fieldName)
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
methodVisitor.visitLdcInsn( persistentField.getName() );
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
managedCtClass.getInternalName(),
|
||||||
|
EnhancerConstants.TRACKER_CHANGER_NAME,
|
||||||
|
Type.getMethodDescriptor( Type.getType( void.class ), Type.getType( String.class ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
// }
|
||||||
|
methodVisitor.visitLabel( skip );
|
||||||
|
if ( implementationContext.getClassFileVersion().isAtLeast( ClassFileVersion.JAVA_V6 ) ) {
|
||||||
|
methodVisitor.visitFrame( Opcodes.F_SAME, 0, null, 0, null );
|
||||||
|
}
|
||||||
|
return new Size( 1 + 2 * persistentField.getType().asErasure().getStackSize().getSize(), instrumentedMethod.getStackSize() );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,314 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.Embedded;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
|
import org.hibernate.engine.spi.CompositeOwner;
|
||||||
|
import org.hibernate.internal.CoreLogging;
|
||||||
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
|
||||||
|
import net.bytebuddy.ClassFileVersion;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||||
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
|
import net.bytebuddy.description.type.TypeDefinition;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import net.bytebuddy.implementation.FieldAccessor;
|
||||||
|
import net.bytebuddy.implementation.Implementation;
|
||||||
|
import net.bytebuddy.implementation.StubMethod;
|
||||||
|
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
|
||||||
|
import net.bytebuddy.jar.asm.MethodVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import net.bytebuddy.jar.asm.Type;
|
||||||
|
import net.bytebuddy.pool.TypePool;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
|
||||||
|
class PersistentAttributeTransformer implements AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper {
|
||||||
|
|
||||||
|
private static final CoreMessageLogger log = CoreLogging.messageLogger( PersistentAttributeTransformer.class );
|
||||||
|
|
||||||
|
private final TypeDescription managedCtClass;
|
||||||
|
|
||||||
|
private final ByteBuddyEnhancementContext enhancementContext;
|
||||||
|
|
||||||
|
private final TypePool classPool;
|
||||||
|
|
||||||
|
private final FieldDescription[] enhancedFields;
|
||||||
|
|
||||||
|
private PersistentAttributeTransformer(
|
||||||
|
TypeDescription managedCtClass,
|
||||||
|
ByteBuddyEnhancementContext enhancementContext,
|
||||||
|
TypePool classPool,
|
||||||
|
FieldDescription[] enhancedFields) {
|
||||||
|
this.managedCtClass = managedCtClass;
|
||||||
|
this.enhancementContext = enhancementContext;
|
||||||
|
this.classPool = classPool;
|
||||||
|
this.enhancedFields = enhancedFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PersistentAttributeTransformer collectPersistentFields(
|
||||||
|
TypeDescription managedCtClass,
|
||||||
|
ByteBuddyEnhancementContext enhancementContext,
|
||||||
|
TypePool classPool) {
|
||||||
|
List<FieldDescription> persistentFieldList = new ArrayList<FieldDescription>();
|
||||||
|
for ( FieldDescription 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 ( !ctField.isStatic() && 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, enhancementContext ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldDescription[] orderedFields = enhancementContext.order( persistentFieldList.toArray( new FieldDescription[0] ) );
|
||||||
|
log.debugf( "Persistent fields for entity %s: %s", managedCtClass.getName(), Arrays.toString( orderedFields ) );
|
||||||
|
return new PersistentAttributeTransformer( managedCtClass, enhancementContext, classPool, orderedFields );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection<FieldDescription> collectInheritPersistentFields(
|
||||||
|
TypeDefinition managedCtClass,
|
||||||
|
ByteBuddyEnhancementContext enhancementContext) {
|
||||||
|
if ( managedCtClass == null || managedCtClass.represents( Object.class ) ) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
TypeDefinition managedCtSuperclass = managedCtClass.getSuperClass();
|
||||||
|
|
||||||
|
if ( !enhancementContext.isMappedSuperclassClass( managedCtSuperclass.asErasure() ) ) {
|
||||||
|
return collectInheritPersistentFields( managedCtSuperclass, enhancementContext );
|
||||||
|
}
|
||||||
|
log.debugf( "Found @MappedSuperclass %s to collectPersistenceFields", managedCtSuperclass );
|
||||||
|
List<FieldDescription> persistentFieldList = new ArrayList<FieldDescription>();
|
||||||
|
|
||||||
|
for ( FieldDescription ctField : managedCtSuperclass.getDeclaredFields() ) {
|
||||||
|
if ( ctField.getName().startsWith( "$$_hibernate_" ) || "this$0".equals( ctField.getName() ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( !ctField.isStatic() && enhancementContext.isPersistentField( ctField ) ) {
|
||||||
|
persistentFieldList.add( ctField );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
persistentFieldList.addAll( collectInheritPersistentFields( managedCtSuperclass, enhancementContext ) );
|
||||||
|
return persistentFieldList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodVisitor wrap(
|
||||||
|
TypeDescription instrumentedType,
|
||||||
|
MethodDescription.InDefinedShape instrumentedMethod,
|
||||||
|
MethodVisitor methodVisitor,
|
||||||
|
Implementation.Context implementationContext,
|
||||||
|
TypePool typePool,
|
||||||
|
int writerFlags,
|
||||||
|
int readerFlags) {
|
||||||
|
return new MethodVisitor( Opcodes.ASM5, methodVisitor ) {
|
||||||
|
@Override
|
||||||
|
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||||
|
if ( isEnhanced( owner, name, desc ) ) {
|
||||||
|
switch ( opcode ) {
|
||||||
|
case Opcodes.GETFIELD:
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
owner,
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + name,
|
||||||
|
"()" + desc,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
case Opcodes.PUTFIELD:
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
owner,
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + name,
|
||||||
|
"(" + desc + ")V",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visitFieldInsn( opcode, owner, name, desc );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEnhanced(String owner, String name, String desc) {
|
||||||
|
for ( FieldDescription enhancedField : enhancedFields ) {
|
||||||
|
if ( enhancedField.getName().equals( name )
|
||||||
|
&& enhancedField.getDescriptor().equals( desc )
|
||||||
|
&& enhancedField.getDeclaringType().asErasure().getInternalName().equals( owner ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicType.Builder<?> applyTo(DynamicType.Builder<?> builder, boolean accessor) {
|
||||||
|
boolean compositeOwner = false;
|
||||||
|
|
||||||
|
builder = builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().method( not( nameStartsWith( "$$_hibernate_" ) ), this ) );
|
||||||
|
for ( FieldDescription enhancedField : enhancedFields ) {
|
||||||
|
builder = builder
|
||||||
|
.defineMethod(
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + enhancedField.getName(),
|
||||||
|
enhancedField.getType().asErasure(),
|
||||||
|
Visibility.PUBLIC
|
||||||
|
)
|
||||||
|
.intercept(
|
||||||
|
accessor
|
||||||
|
? FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() )
|
||||||
|
: fieldReader( enhancedField )
|
||||||
|
)
|
||||||
|
.defineMethod(
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + enhancedField.getName(),
|
||||||
|
TypeDescription.VOID,
|
||||||
|
Visibility.PUBLIC
|
||||||
|
)
|
||||||
|
.withParameters( enhancedField.getType().asErasure() )
|
||||||
|
.intercept( accessor
|
||||||
|
? FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() )
|
||||||
|
: fieldWriter( enhancedField ) );
|
||||||
|
|
||||||
|
if ( !compositeOwner
|
||||||
|
&& !accessor
|
||||||
|
&& EnhancerImpl.isAnnotationPresent( enhancedField, Embedded.class )
|
||||||
|
&& enhancementContext.isCompositeClass( enhancedField.getType().asErasure() )
|
||||||
|
&& enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
|
||||||
|
compositeOwner = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( compositeOwner ) {
|
||||||
|
builder = builder.implement( CompositeOwner.class );
|
||||||
|
|
||||||
|
if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
|
||||||
|
builder = builder.defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC )
|
||||||
|
.withParameters( String.class )
|
||||||
|
.intercept( Advice.to( CodeTemplates.CompositeOwnerDirtyCheckingHandler.class ).wrap( StubMethod.INSTANCE ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
|
||||||
|
builder = applyExtended( builder );
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Implementation fieldReader(FieldDescription enhancedField) {
|
||||||
|
if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) || !enhancementContext.isLazyLoadable( enhancedField ) ) {
|
||||||
|
if ( enhancedField.getDeclaringType().asErasure().equals( managedCtClass ) ) {
|
||||||
|
return FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new Implementation.Simple( new FieldMethodReader( managedCtClass, enhancedField ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new Implementation.Simple( FieldReaderAppender.of( managedCtClass, enhancedField ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Implementation fieldWriter(FieldDescription enhancedField) {
|
||||||
|
Implementation implementation;
|
||||||
|
if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) || !enhancementContext.isLazyLoadable( enhancedField ) ) {
|
||||||
|
if ( enhancedField.getDeclaringType().asErasure().equals( managedCtClass ) ) {
|
||||||
|
implementation = FieldAccessor.ofField( enhancedField.getName() ).in( enhancedField.getDeclaringType().asErasure() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
implementation = new Implementation.Simple( new FieldMethodWriter( managedCtClass, enhancedField ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
implementation = new Implementation.Simple( FieldWriterAppender.of( managedCtClass, enhancedField ) );
|
||||||
|
}
|
||||||
|
implementation = InlineDirtyCheckingHandler.wrap( managedCtClass, enhancementContext, enhancedField, implementation );
|
||||||
|
return BiDirectionalAssociationHandler.wrap( managedCtClass, enhancementContext, enhancedField, implementation );
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicType.Builder<?> applyExtended(DynamicType.Builder<?> builder) {
|
||||||
|
AsmVisitorWrapper.ForDeclaredMethods.MethodVisitorWrapper enhancer = new FieldAccessEnhancer( managedCtClass, enhancementContext, classPool );
|
||||||
|
return builder.visit( new AsmVisitorWrapper.ForDeclaredMethods().method( not( nameStartsWith( "$$_hibernate_" ) ), enhancer ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FieldMethodReader implements ByteCodeAppender {
|
||||||
|
|
||||||
|
private final TypeDescription managedCtClass;
|
||||||
|
|
||||||
|
private final FieldDescription persistentField;
|
||||||
|
|
||||||
|
private FieldMethodReader(TypeDescription managedCtClass, FieldDescription persistentField) {
|
||||||
|
this.managedCtClass = managedCtClass;
|
||||||
|
this.persistentField = persistentField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size apply(
|
||||||
|
MethodVisitor methodVisitor,
|
||||||
|
Implementation.Context implementationContext,
|
||||||
|
MethodDescription instrumentedMethod
|
||||||
|
) {
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESPECIAL,
|
||||||
|
managedCtClass.getSuperClass().asErasure().getInternalName(),
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + persistentField.getName(),
|
||||||
|
Type.getMethodDescriptor( Type.getType( persistentField.getType().asErasure().getDescriptor() ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
methodVisitor.visitInsn( Type.getType( persistentField.getType().asErasure().getDescriptor() ).getOpcode( Opcodes.IRETURN ) );
|
||||||
|
return new Size( persistentField.getType().getStackSize().getSize(), instrumentedMethod.getStackSize() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FieldMethodWriter implements ByteCodeAppender {
|
||||||
|
|
||||||
|
private final TypeDescription managedCtClass;
|
||||||
|
|
||||||
|
private final FieldDescription persistentField;
|
||||||
|
|
||||||
|
private FieldMethodWriter(TypeDescription managedCtClass, FieldDescription persistentField) {
|
||||||
|
this.managedCtClass = managedCtClass;
|
||||||
|
this.persistentField = persistentField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size apply(
|
||||||
|
MethodVisitor methodVisitor,
|
||||||
|
Implementation.Context implementationContext,
|
||||||
|
MethodDescription instrumentedMethod
|
||||||
|
) {
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 0 );
|
||||||
|
methodVisitor.visitVarInsn( Type.getType( persistentField.getType().asErasure().getDescriptor() ).getOpcode( Opcodes.ILOAD ), 1 );
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESPECIAL,
|
||||||
|
managedCtClass.getSuperClass().asErasure().getInternalName(),
|
||||||
|
EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + persistentField.getName(),
|
||||||
|
Type.getMethodDescriptor( Type.getType( void.class ), Type.getType( persistentField.getType().asErasure().getDescriptor() ) ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
methodVisitor.visitInsn( Opcodes.RETURN );
|
||||||
|
return new Size( 1 + persistentField.getType().getStackSize().getSize(), instrumentedMethod.getStackSize() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedField;
|
||||||
|
|
||||||
|
import net.bytebuddy.description.field.FieldDescription;
|
||||||
|
|
||||||
|
class UnloadedFieldDescription implements UnloadedField {
|
||||||
|
|
||||||
|
final FieldDescription fieldDescription;
|
||||||
|
|
||||||
|
UnloadedFieldDescription(FieldDescription fieldDescription) {
|
||||||
|
this.fieldDescription = fieldDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
return fieldDescription.getDeclaredAnnotations().isAnnotationPresent( annotationType );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedField;
|
||||||
|
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
|
||||||
|
class UnloadedTypeDescription implements UnloadedClass {
|
||||||
|
|
||||||
|
private final TypeDescription typeDescription;
|
||||||
|
|
||||||
|
UnloadedTypeDescription(TypeDescription typeDescription) {
|
||||||
|
this.typeDescription = typeDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
return typeDescription.getDeclaredAnnotations().isAnnotationPresent( annotationType );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return typeDescription.getName();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.bytebuddy;
|
|
@ -4,7 +4,7 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* 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>.
|
* 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;
|
package org.hibernate.bytecode.enhance.internal.javassist;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -37,7 +37,7 @@ public abstract class AttributeTypeDescriptor {
|
||||||
|
|
||||||
public abstract String buildWriteInterceptionBodyFragment(String fieldName);
|
public abstract String buildWriteInterceptionBodyFragment(String fieldName);
|
||||||
|
|
||||||
public String buildInLineDirtyCheckingBodyFragment(EnhancementContext context, CtField currentValue) {
|
public String buildInLineDirtyCheckingBodyFragment(JavassistEnhancementContext context, CtField currentValue) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
try {
|
try {
|
||||||
// should ignore primary keys
|
// should ignore primary keys
|
|
@ -4,10 +4,11 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* 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>.
|
* 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;
|
package org.hibernate.bytecode.enhance.internal.javassist;
|
||||||
|
|
||||||
import javassist.CannotCompileException;
|
import javassist.CannotCompileException;
|
||||||
import javassist.CtClass;
|
import javassist.CtClass;
|
||||||
|
|
||||||
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
|
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
|
@ -22,7 +23,7 @@ import org.hibernate.engine.spi.ManagedComposite;
|
||||||
*/
|
*/
|
||||||
public class CompositeEnhancer extends PersistentAttributesEnhancer {
|
public class CompositeEnhancer extends PersistentAttributesEnhancer {
|
||||||
|
|
||||||
public CompositeEnhancer(EnhancementContext context) {
|
public CompositeEnhancer(JavassistEnhancementContext context) {
|
||||||
super( context );
|
super( context );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,10 +58,10 @@ public class CompositeEnhancer extends PersistentAttributesEnhancer {
|
||||||
" if (%2$s == null) { %2$s = new %4$s(); }%n" +
|
" if (%2$s == null) { %2$s = new %4$s(); }%n" +
|
||||||
" %2$s.add(name, tracker);%n" +
|
" %2$s.add(name, tracker);%n" +
|
||||||
"}",
|
"}",
|
||||||
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
|
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
|
||||||
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME,
|
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME,
|
||||||
CompositeOwner.class.getName(),
|
CompositeOwner.class.getName(),
|
||||||
CompositeOwnerTracker.class.getName() );
|
CompositeOwnerTracker.class.getName() );
|
||||||
|
|
||||||
MethodWriter.write( managedCtClass, "" +
|
MethodWriter.write( managedCtClass, "" +
|
||||||
"public void %1$s(String name) {%n" +
|
"public void %1$s(String name) {%n" +
|
|
@ -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.bytecode.enhance.internal.javassist;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javassist.CannotCompileException;
|
||||||
|
import javassist.ClassPool;
|
||||||
|
import javassist.CtClass;
|
||||||
|
import javassist.LoaderClassPath;
|
||||||
|
|
||||||
|
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.ManagedComposite;
|
||||||
|
import org.hibernate.engine.spi.ManagedEntity;
|
||||||
|
import org.hibernate.engine.spi.ManagedMappedSuperclass;
|
||||||
|
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 {
|
||||||
|
try {
|
||||||
|
final CtClass managedCtClass = classPool.makeClassIfNew( new ByteArrayInputStream( originalBytes ) );
|
||||||
|
if ( enhance( managedCtClass ) ) {
|
||||||
|
return getByteCode( managedCtClass );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
log.unableToBuildEnhancementMetamodel( className );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] enhance(File javaClassFile) throws EnhancementException, IOException {
|
||||||
|
final CtClass ctClass = classPool.makeClass( new FileInputStream( javaClassFile ) );
|
||||||
|
try {
|
||||||
|
return enhance( ctClass.getName(), ctClass.toBytecode() );
|
||||||
|
}
|
||||||
|
catch (CannotCompileException e) {
|
||||||
|
log.warn( "Unable to enhance class file [" + javaClassFile.getAbsolutePath() + "]", e );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassPool buildClassPool(final JavassistEnhancementContext enhancementContext) {
|
||||||
|
final ClassPool classPool = new ClassPool( false ) {
|
||||||
|
@Override
|
||||||
|
public ClassLoader getClassLoader() {
|
||||||
|
return enhancementContext.getLoadingClassLoader();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final 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 );
|
||||||
|
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.infof( "Enhancing [%s] as Entity", managedCtClass.getName() );
|
||||||
|
new EntityEnhancer( enhancementContext ).enhance( managedCtClass );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
|
||||||
|
log.infof( "Enhancing [%s] as Composite", managedCtClass.getName() );
|
||||||
|
new CompositeEnhancer( enhancementContext ).enhance( managedCtClass );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
|
||||||
|
log.infof( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() );
|
||||||
|
new MappedSuperclassEnhancer( enhancementContext ).enhance( managedCtClass );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
|
||||||
|
log.infof( "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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean alreadyEnhanced(CtClass managedCtClass) {
|
||||||
|
if ( !PersistentAttributesHelper.isAssignable( managedCtClass, Managed.class.getName() ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// HHH-10977 - When a mapped superclass gets enhanced before a subclassing entity, the entity does not get enhanced, but it implements the Managed interface
|
||||||
|
return enhancementContext.isEntityClass( managedCtClass ) && PersistentAttributesHelper.isAssignable( managedCtClass, ManagedEntity.class.getName() )
|
||||||
|
|| enhancementContext.isCompositeClass( managedCtClass ) && PersistentAttributesHelper.isAssignable(
|
||||||
|
managedCtClass,
|
||||||
|
ManagedComposite.class.getName()
|
||||||
|
)
|
||||||
|
|| enhancementContext.isMappedSuperclassClass( managedCtClass ) && PersistentAttributesHelper.isAssignable(
|
||||||
|
managedCtClass,
|
||||||
|
ManagedMappedSuperclass.class.getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getByteCode(CtClass managedCtClass) {
|
||||||
|
final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||||
|
final 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() );
|
||||||
|
}
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,12 +4,11 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* 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>.
|
* 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;
|
package org.hibernate.bytecode.enhance.internal.javassist;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -20,6 +19,7 @@ import javassist.CtField;
|
||||||
import javassist.Modifier;
|
import javassist.Modifier;
|
||||||
|
|
||||||
import javassist.NotFoundException;
|
import javassist.NotFoundException;
|
||||||
|
|
||||||
import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker;
|
import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker;
|
||||||
import org.hibernate.bytecode.enhance.internal.tracker.SimpleCollectionTracker;
|
import org.hibernate.bytecode.enhance.internal.tracker.SimpleCollectionTracker;
|
||||||
import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker;
|
import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker;
|
||||||
|
@ -40,7 +40,7 @@ import org.hibernate.engine.spi.SelfDirtinessTracker;
|
||||||
*/
|
*/
|
||||||
public class EntityEnhancer extends PersistentAttributesEnhancer {
|
public class EntityEnhancer extends PersistentAttributesEnhancer {
|
||||||
|
|
||||||
public EntityEnhancer(EnhancementContext context) {
|
public EntityEnhancer(JavassistEnhancementContext context) {
|
||||||
super( context );
|
super( context );
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* 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>.
|
* 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;
|
package org.hibernate.bytecode.enhance.internal.javassist;
|
||||||
|
|
||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
|
|
|
@ -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 ) );
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,11 +4,12 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* 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>.
|
* 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;
|
package org.hibernate.bytecode.enhance.internal.javassist;
|
||||||
|
|
||||||
import javassist.CtClass;
|
import javassist.CtClass;
|
||||||
import javassist.CtField;
|
import javassist.CtField;
|
||||||
import javassist.CtMethod;
|
import javassist.CtMethod;
|
||||||
|
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
import org.hibernate.engine.spi.ManagedMappedSuperclass;
|
import org.hibernate.engine.spi.ManagedMappedSuperclass;
|
||||||
|
@ -20,7 +21,7 @@ import org.hibernate.engine.spi.ManagedMappedSuperclass;
|
||||||
*/
|
*/
|
||||||
public class MappedSuperclassEnhancer extends PersistentAttributesEnhancer {
|
public class MappedSuperclassEnhancer extends PersistentAttributesEnhancer {
|
||||||
|
|
||||||
public MappedSuperclassEnhancer(EnhancementContext context) {
|
public MappedSuperclassEnhancer(JavassistEnhancementContext context) {
|
||||||
super( context );
|
super( context );
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* 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>.
|
* 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;
|
package org.hibernate.bytecode.enhance.internal.javassist;
|
||||||
|
|
||||||
import javassist.CannotCompileException;
|
import javassist.CannotCompileException;
|
||||||
import javassist.CtClass;
|
import javassist.CtClass;
|
|
@ -4,14 +4,13 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* 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>.
|
* 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;
|
package org.hibernate.bytecode.enhance.internal.javassist;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.persistence.Embedded;
|
import javax.persistence.Embedded;
|
||||||
|
@ -36,9 +35,7 @@ import javassist.bytecode.Opcode;
|
||||||
import javassist.bytecode.stackmap.MapMaker;
|
import javassist.bytecode.stackmap.MapMaker;
|
||||||
|
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementException;
|
import org.hibernate.bytecode.enhance.spi.EnhancementException;
|
||||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
|
||||||
import org.hibernate.engine.spi.CompositeOwner;
|
import org.hibernate.engine.spi.CompositeOwner;
|
||||||
import org.hibernate.engine.spi.CompositeTracker;
|
import org.hibernate.engine.spi.CompositeTracker;
|
||||||
|
@ -50,11 +47,11 @@ import org.hibernate.internal.CoreMessageLogger;
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
* @author <a href="mailto:lbarreiro@redhat.com">Luis Barreiro</a>
|
||||||
*/
|
*/
|
||||||
public class PersistentAttributesEnhancer extends Enhancer {
|
public class PersistentAttributesEnhancer extends EnhancerImpl {
|
||||||
|
|
||||||
private static final CoreMessageLogger log = CoreLogging.messageLogger( PersistentAttributesEnhancer.class );
|
private static final CoreMessageLogger log = CoreLogging.messageLogger( PersistentAttributesEnhancer.class );
|
||||||
|
|
||||||
public PersistentAttributesEnhancer(EnhancementContext context) {
|
public PersistentAttributesEnhancer(JavassistEnhancementContext context) {
|
||||||
super( context );
|
super( context );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +94,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
}
|
}
|
||||||
|
|
||||||
CtField[] orderedFields = enhancementContext.order( persistentFieldList.toArray( new CtField[0] ) );
|
CtField[] orderedFields = enhancementContext.order( persistentFieldList.toArray( new CtField[0] ) );
|
||||||
log.debugf( "Persistent fields for entity %s: %s", managedCtClass.getName(), Arrays.toString( orderedFields ));
|
log.debugf( "Persistent fields for entity %s: %s", managedCtClass.getName(), Arrays.toString( orderedFields ) );
|
||||||
return orderedFields;
|
return orderedFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +128,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PersistentAttributeAccessMethods enhancePersistentAttribute( CtClass managedCtClass, CtField persistentField) {
|
private PersistentAttributeAccessMethods enhancePersistentAttribute(CtClass managedCtClass, CtField persistentField) {
|
||||||
try {
|
try {
|
||||||
AttributeTypeDescriptor typeDescriptor = AttributeTypeDescriptor.resolve( managedCtClass, persistentField );
|
AttributeTypeDescriptor typeDescriptor = AttributeTypeDescriptor.resolve( managedCtClass, persistentField );
|
||||||
return new PersistentAttributeAccessMethods(
|
return new PersistentAttributeAccessMethods(
|
||||||
|
@ -165,13 +162,13 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
String declaredReadFragment = "this." + fieldName + "";
|
String declaredReadFragment = "this." + fieldName + "";
|
||||||
String superReadFragment = "super." + readerName + "()";
|
String superReadFragment = "super." + readerName + "()";
|
||||||
|
|
||||||
if (!declared) {
|
if ( !declared ) {
|
||||||
// create a temporary getter on the supper entity to be able to compile our code
|
// create a temporary getter on the supper entity to be able to compile our code
|
||||||
try {
|
try {
|
||||||
persistentField.getDeclaringClass().getDeclaredMethod( readerName );
|
persistentField.getDeclaringClass().getDeclaredMethod( readerName );
|
||||||
persistentField.getDeclaringClass().getDeclaredMethod( writerName );
|
persistentField.getDeclaringClass().getDeclaredMethod( writerName );
|
||||||
}
|
}
|
||||||
catch (NotFoundException nfe){
|
catch ( NotFoundException nfe ) {
|
||||||
tmpSuperReader = MethodWriter.addGetter( persistentField.getDeclaringClass(), persistentField.getName(), readerName );
|
tmpSuperReader = MethodWriter.addGetter( persistentField.getDeclaringClass(), persistentField.getName(), readerName );
|
||||||
tmpSuperWriter = MethodWriter.addSetter( persistentField.getDeclaringClass(), persistentField.getName(), writerName );
|
tmpSuperWriter = MethodWriter.addSetter( persistentField.getDeclaringClass(), persistentField.getName(), writerName );
|
||||||
}
|
}
|
||||||
|
@ -179,8 +176,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
|
|
||||||
// read attempts only have to deal lazy-loading support, not dirty checking;
|
// 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
|
// so if the field is not enabled as lazy-loadable return a plain simple getter as the reader
|
||||||
if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass )
|
if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) || !enhancementContext.isLazyLoadable( persistentField ) ) {
|
||||||
|| !enhancementContext.isLazyLoadable( persistentField ) ) {
|
|
||||||
reader = MethodWriter.write(
|
reader = MethodWriter.write(
|
||||||
managedCtClass, "public %s %s() { return %s;%n}",
|
managedCtClass, "public %s %s() { return %s;%n}",
|
||||||
persistentField.getType().getName(),
|
persistentField.getType().getName(),
|
||||||
|
@ -237,15 +233,15 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
try {
|
try {
|
||||||
boolean declared = persistentField.getDeclaringClass().equals( managedCtClass );
|
boolean declared = persistentField.getDeclaringClass().equals( managedCtClass );
|
||||||
String declaredWriteFragment = "this." + fieldName + "=" + fieldName + ";";
|
String declaredWriteFragment = "this." + fieldName + "=" + fieldName + ";";
|
||||||
String superWriteFragment = "super." + writerName + "(" + fieldName + ");";
|
String superWriteFragment = "super." + writerName + "(" + fieldName + ");";
|
||||||
|
|
||||||
if (!declared) {
|
if ( !declared ) {
|
||||||
// create a temporary setter on the supper entity to be able to compile our code
|
// create a temporary setter on the supper entity to be able to compile our code
|
||||||
try {
|
try {
|
||||||
persistentField.getDeclaringClass().getDeclaredMethod( readerName );
|
persistentField.getDeclaringClass().getDeclaredMethod( readerName );
|
||||||
persistentField.getDeclaringClass().getDeclaredMethod( writerName );
|
persistentField.getDeclaringClass().getDeclaredMethod( writerName );
|
||||||
}
|
}
|
||||||
catch (NotFoundException nfe){
|
catch ( NotFoundException nfe ) {
|
||||||
tmpSuperReader = MethodWriter.addGetter( persistentField.getDeclaringClass(), persistentField.getName(), readerName );
|
tmpSuperReader = MethodWriter.addGetter( persistentField.getDeclaringClass(), persistentField.getName(), readerName );
|
||||||
tmpSuperWriter = MethodWriter.addSetter( persistentField.getDeclaringClass(), persistentField.getName(), writerName );
|
tmpSuperWriter = MethodWriter.addSetter( persistentField.getDeclaringClass(), persistentField.getName(), writerName );
|
||||||
}
|
}
|
||||||
|
@ -352,7 +348,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
getter = targetEntity.getDeclaredMethod( mappedByGetterName );
|
getter = targetEntity.getDeclaredMethod( mappedByGetterName );
|
||||||
setter = targetEntity.getDeclaredMethod( mappedByGetterName );
|
setter = targetEntity.getDeclaredMethod( mappedByGetterName );
|
||||||
}
|
}
|
||||||
catch (NotFoundException nfe){
|
catch ( NotFoundException nfe ) {
|
||||||
getter = MethodWriter.addGetter( targetEntity, mappedBy, mappedByGetterName );
|
getter = MethodWriter.addGetter( targetEntity, mappedBy, mappedByGetterName );
|
||||||
setter = MethodWriter.addSetter( targetEntity, mappedBy, mappedBySetterName );
|
setter = MethodWriter.addSetter( targetEntity, mappedBy, mappedBySetterName );
|
||||||
tmpTargetMethods = true;
|
tmpTargetMethods = true;
|
||||||
|
@ -397,19 +393,19 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
}
|
}
|
||||||
if ( PersistentAttributesHelper.hasAnnotation( persistentField, OneToMany.class ) ) {
|
if ( PersistentAttributesHelper.hasAnnotation( persistentField, OneToMany.class ) ) {
|
||||||
boolean isMap = PersistentAttributesHelper.isAssignable( persistentField.getType(), Map.class.getName() );
|
boolean isMap = PersistentAttributesHelper.isAssignable( persistentField.getType(), Map.class.getName() );
|
||||||
String toArrayMethod = isMap ? "values().toArray()" : "toArray()" ;
|
String toArrayMethod = isMap ? "values().toArray()" : "toArray()";
|
||||||
|
|
||||||
// only remove elements not in the new collection or else we would loose those elements
|
// only remove elements not in the new collection or else we would loose those elements
|
||||||
// don't use iterator to avoid ConcurrentModException
|
// don't use iterator to avoid ConcurrentModException
|
||||||
fieldWriter.insertBefore(
|
fieldWriter.insertBefore(
|
||||||
String.format(
|
String.format(
|
||||||
" if (this.%3$s != null && %1$s) {%n" +
|
" if (this.%3$s != null && %1$s) {%n" +
|
||||||
" Object[] array = this.%3$s.%2$s;%n" +
|
" Object[] array = this.%3$s.%2$s;%n" +
|
||||||
" for (int i = 0; i < array.length; i++) {%n" +
|
" for (int i = 0; i < array.length; i++) {%n" +
|
||||||
" %4$s target = (%4$s) array[i];%n" +
|
" %4$s target = (%4$s) array[i];%n" +
|
||||||
" if ($1 == null || !$1.contains(target)) { target.%5$s(null); }%n" +
|
" if ($1 == null || !$1.contains(target)) { target.%5$s(null); }%n" +
|
||||||
" }%n" +
|
" }%n" +
|
||||||
" }%n",
|
" }%n",
|
||||||
currentAssociationLoaded,
|
currentAssociationLoaded,
|
||||||
toArrayMethod,
|
toArrayMethod,
|
||||||
persistentField.getName(),
|
persistentField.getName(),
|
||||||
|
@ -420,12 +416,12 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
fieldWriter.insertAfter(
|
fieldWriter.insertAfter(
|
||||||
String.format(
|
String.format(
|
||||||
" if ($1 != null && %1$s) {%n" +
|
" if ($1 != null && %1$s) {%n" +
|
||||||
" Object[] array = $1.%2$s;%n" +
|
" Object[] array = $1.%2$s;%n" +
|
||||||
" for (int i = 0; i < array.length; i++) {%n" +
|
" for (int i = 0; i < array.length; i++) {%n" +
|
||||||
" %4$s target = (%4$s) array[i];%n" +
|
" %4$s target = (%4$s) array[i];%n" +
|
||||||
" if (%3$s && target.%5$s() != this) { target.%6$s(this); }%n" +
|
" if (%3$s && target.%5$s() != this) { target.%6$s(this); }%n" +
|
||||||
" }%n" +
|
" }%n" +
|
||||||
" }%n",
|
" }%n",
|
||||||
newAssociationLoaded,
|
newAssociationLoaded,
|
||||||
toArrayMethod,
|
toArrayMethod,
|
||||||
targetElementLoaded,
|
targetElementLoaded,
|
||||||
|
@ -448,9 +444,9 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
fieldWriter.insertAfter(
|
fieldWriter.insertAfter(
|
||||||
String.format(
|
String.format(
|
||||||
" if ($1 != null && %s) {%n" +
|
" if ($1 != null && %s) {%n" +
|
||||||
" java.util.Collection c = $1.%s();%n" +
|
" java.util.Collection c = $1.%s();%n" +
|
||||||
" if (c != null && !c.contains(this)) { c.add(this); }%n" +
|
" if (c != null && !c.contains(this)) { c.add(this); }%n" +
|
||||||
" }%n",
|
" }%n",
|
||||||
newAssociationLoaded,
|
newAssociationLoaded,
|
||||||
mappedByGetterName
|
mappedByGetterName
|
||||||
)
|
)
|
||||||
|
@ -458,7 +454,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
}
|
}
|
||||||
if ( PersistentAttributesHelper.hasAnnotation( persistentField, ManyToMany.class ) ) {
|
if ( PersistentAttributesHelper.hasAnnotation( persistentField, ManyToMany.class ) ) {
|
||||||
if ( PersistentAttributesHelper.isAssignable( persistentField.getType(), Map.class.getName() ) ||
|
if ( PersistentAttributesHelper.isAssignable( persistentField.getType(), Map.class.getName() ) ||
|
||||||
PersistentAttributesHelper.isAssignable( targetEntity.getField( mappedBy ).getType() , Map.class.getName() ) ) {
|
PersistentAttributesHelper.isAssignable( targetEntity.getField( mappedBy ).getType(), Map.class.getName() ) ) {
|
||||||
log.infof(
|
log.infof(
|
||||||
"Bi-directional association for field [%s#%s] not managed: @ManyToMany in java.util.Map attribute not supported ",
|
"Bi-directional association for field [%s#%s] not managed: @ManyToMany in java.util.Map attribute not supported ",
|
||||||
managedCtClass.getName(),
|
managedCtClass.getName(),
|
||||||
|
@ -469,12 +465,12 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
fieldWriter.insertBefore(
|
fieldWriter.insertBefore(
|
||||||
String.format(
|
String.format(
|
||||||
" if (this.%2$s != null && %1$s) {%n" +
|
" if (this.%2$s != null && %1$s) {%n" +
|
||||||
" Object[] array = this.%2$s.toArray();%n" +
|
" Object[] array = this.%2$s.toArray();%n" +
|
||||||
" for (int i = 0; i < array.length; i++) {%n" +
|
" for (int i = 0; i < array.length; i++) {%n" +
|
||||||
" %3$s target = (%3$s) array[i];%n" +
|
" %3$s target = (%3$s) array[i];%n" +
|
||||||
" if ($1 == null || !$1.contains(target)) { target.%4$s().remove(this); }%n" +
|
" if ($1 == null || !$1.contains(target)) { target.%4$s().remove(this); }%n" +
|
||||||
" }%n" +
|
" }%n" +
|
||||||
" }%n",
|
" }%n",
|
||||||
currentAssociationLoaded,
|
currentAssociationLoaded,
|
||||||
persistentField.getName(),
|
persistentField.getName(),
|
||||||
targetEntity.getName(),
|
targetEntity.getName(),
|
||||||
|
@ -484,15 +480,15 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
fieldWriter.insertAfter(
|
fieldWriter.insertAfter(
|
||||||
String.format(
|
String.format(
|
||||||
" if ($1 != null && %s) {%n" +
|
" if ($1 != null && %s) {%n" +
|
||||||
" Object[] array = $1.toArray();%n" +
|
" Object[] array = $1.toArray();%n" +
|
||||||
" for (int i = 0; i < array.length; i++) {%n" +
|
" for (int i = 0; i < array.length; i++) {%n" +
|
||||||
" %s target = (%s) array[i];%n" +
|
" %s target = (%s) array[i];%n" +
|
||||||
" if (%s) {%n" +
|
" if (%s) {%n" +
|
||||||
" java.util.Collection c = target.%s();%n" +
|
" java.util.Collection c = target.%s();%n" +
|
||||||
" if (c != this && c != null) { c.add(this); }%n" +
|
" if (c != this && c != null) { c.add(this); }%n" +
|
||||||
" }%n" +
|
" }%n" +
|
||||||
" }%n" +
|
" }%n" +
|
||||||
" }%n",
|
" }%n",
|
||||||
newAssociationLoaded,
|
newAssociationLoaded,
|
||||||
targetEntity.getName(),
|
targetEntity.getName(),
|
||||||
targetEntity.getName(),
|
targetEntity.getName(),
|
||||||
|
@ -523,7 +519,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
// cleanup previous owner
|
// cleanup previous owner
|
||||||
fieldWriter.insertBefore(
|
fieldWriter.insertBefore(
|
||||||
String.format(
|
String.format(
|
||||||
"if (%1$s != null) { ((%2$s) %1$s).%3$s(\"%1$s\"); }%n",
|
"if (%1$s != null) { ((%2$s) %1$s).%3$s(\"%1$s\"); }%n",
|
||||||
persistentField.getName(),
|
persistentField.getName(),
|
||||||
CompositeTracker.class.getName(),
|
CompositeTracker.class.getName(),
|
||||||
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER
|
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER
|
||||||
|
@ -533,7 +529,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
// trigger track changes
|
// trigger track changes
|
||||||
fieldWriter.insertAfter(
|
fieldWriter.insertAfter(
|
||||||
String.format(
|
String.format(
|
||||||
"if (%1$s != null) { ((%2$s) %1$s).%4$s(\"%1$s\", (%3$s) this); }%n" +
|
"if (%1$s != null) { ((%2$s) %1$s).%4$s(\"%1$s\", (%3$s) this); }%n" +
|
||||||
"%5$s(\"%1$s\");",
|
"%5$s(\"%1$s\");",
|
||||||
persistentField.getName(),
|
persistentField.getName(),
|
||||||
CompositeTracker.class.getName(),
|
CompositeTracker.class.getName(),
|
||||||
|
@ -691,11 +687,13 @@ public class PersistentAttributesEnhancer extends Enhancer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debugf( "Extended enhancement: Transforming access to field [%s.%s] from method [%s#%s]",
|
log.debugf(
|
||||||
fieldClassName,
|
"Extended enhancement: Transforming access to field [%s.%s] from method [%s#%s]",
|
||||||
fieldName,
|
fieldClassName,
|
||||||
aCtClass.getName(),
|
fieldName,
|
||||||
methodName );
|
aCtClass.getName(),
|
||||||
|
methodName
|
||||||
|
);
|
||||||
|
|
||||||
if ( op == Opcode.GETFIELD ) {
|
if ( op == Opcode.GETFIELD ) {
|
||||||
int fieldReaderMethodIndex = constPool.addMethodrefInfo(
|
int fieldReaderMethodIndex = constPool.addMethodrefInfo(
|
|
@ -4,7 +4,7 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* 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>.
|
* 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;
|
package org.hibernate.bytecode.enhance.internal.javassist;
|
||||||
|
|
||||||
import java.beans.Introspector;
|
import java.beans.Introspector;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
@ -207,7 +207,7 @@ public class PersistentAttributesHelper {
|
||||||
PersistentAttributesHelper.hasAnnotation( persistentField, ManyToMany.class );
|
PersistentAttributesHelper.hasAnnotation( persistentField, ManyToMany.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getMappedBy(CtField persistentField, CtClass targetEntity, EnhancementContext context) throws NotFoundException {
|
public static String getMappedBy(CtField persistentField, CtClass targetEntity, JavassistEnhancementContext context) throws NotFoundException {
|
||||||
final String local = getMappedByFromAnnotation( persistentField );
|
final String local = getMappedByFromAnnotation( persistentField );
|
||||||
return local.isEmpty() ? getMappedByFromTargetEntity( persistentField, targetEntity, context ) : local;
|
return local.isEmpty() ? getMappedByFromTargetEntity( persistentField, targetEntity, context ) : local;
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ public class PersistentAttributesHelper {
|
||||||
private static String getMappedByFromTargetEntity(
|
private static String getMappedByFromTargetEntity(
|
||||||
CtField persistentField,
|
CtField persistentField,
|
||||||
CtClass targetEntity,
|
CtClass targetEntity,
|
||||||
EnhancementContext context) throws NotFoundException {
|
JavassistEnhancementContext context) throws NotFoundException {
|
||||||
// get mappedBy value by searching in the fields of the target entity class
|
// get mappedBy value by searching in the fields of the target entity class
|
||||||
for ( CtField f : targetEntity.getDeclaredFields() ) {
|
for ( CtField f : targetEntity.getDeclaredFields() ) {
|
||||||
if ( context.isPersistentField( f )
|
if ( context.isPersistentField( f )
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
@ -6,6 +6,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* package containing bytecode enhancement code (internals)
|
* package containing bytecode enhancement (internals)
|
||||||
*/
|
*/
|
||||||
package org.hibernate.bytecode.enhance.internal;
|
package org.hibernate.bytecode.enhance.internal;
|
||||||
|
|
|
@ -14,9 +14,6 @@ import javax.persistence.MappedSuperclass;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
|
|
||||||
import javassist.CtClass;
|
|
||||||
import javassist.CtField;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* default implementation of EnhancementContext. May be sub-classed as needed.
|
* default implementation of EnhancementContext. May be sub-classed as needed.
|
||||||
*
|
*
|
||||||
|
@ -27,6 +24,7 @@ public class DefaultEnhancementContext implements EnhancementContext {
|
||||||
/**
|
/**
|
||||||
* @return the classloader for this class
|
* @return the classloader for this class
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public ClassLoader getLoadingClassLoader() {
|
public ClassLoader getLoadingClassLoader() {
|
||||||
return getClass().getClassLoader();
|
return getClass().getClassLoader();
|
||||||
}
|
}
|
||||||
|
@ -34,77 +32,88 @@ public class DefaultEnhancementContext implements EnhancementContext {
|
||||||
/**
|
/**
|
||||||
* look for @Entity annotation
|
* look for @Entity annotation
|
||||||
*/
|
*/
|
||||||
public boolean isEntityClass(CtClass classDescriptor) {
|
@Override
|
||||||
|
public boolean isEntityClass(UnloadedClass classDescriptor) {
|
||||||
return classDescriptor.hasAnnotation( Entity.class );
|
return classDescriptor.hasAnnotation( Entity.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* look for @Embeddable annotation
|
* look for @Embeddable annotation
|
||||||
*/
|
*/
|
||||||
public boolean isCompositeClass(CtClass classDescriptor) {
|
@Override
|
||||||
|
public boolean isCompositeClass(UnloadedClass classDescriptor) {
|
||||||
return classDescriptor.hasAnnotation( Embeddable.class );
|
return classDescriptor.hasAnnotation( Embeddable.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* look for @MappedSuperclass annotation
|
* look for @MappedSuperclass annotation
|
||||||
*/
|
*/
|
||||||
public boolean isMappedSuperclassClass(CtClass classDescriptor) {
|
@Override
|
||||||
|
public boolean isMappedSuperclassClass(UnloadedClass classDescriptor) {
|
||||||
return classDescriptor.hasAnnotation( MappedSuperclass.class );
|
return classDescriptor.hasAnnotation( MappedSuperclass.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true
|
* @return true
|
||||||
*/
|
*/
|
||||||
public boolean doBiDirectionalAssociationManagement(CtField field) {
|
@Override
|
||||||
|
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true
|
* @return true
|
||||||
*/
|
*/
|
||||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
@Override
|
||||||
|
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return false
|
* @return false
|
||||||
*/
|
*/
|
||||||
public boolean doExtendedEnhancement(CtClass classDescriptor) {
|
@Override
|
||||||
|
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true
|
* @return true
|
||||||
*/
|
*/
|
||||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
@Override
|
||||||
|
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true
|
* @return true
|
||||||
*/
|
*/
|
||||||
public boolean isLazyLoadable(CtField field) {
|
@Override
|
||||||
|
public boolean isLazyLoadable(UnloadedField field) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* look for @Transient annotation
|
* look for @Transient annotation
|
||||||
*/
|
*/
|
||||||
public boolean isPersistentField(CtField ctField) {
|
@Override
|
||||||
|
public boolean isPersistentField(UnloadedField ctField) {
|
||||||
return ! ctField.hasAnnotation( Transient.class );
|
return ! ctField.hasAnnotation( Transient.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* look for @OneToMany, @ManyToMany and @ElementCollection annotations
|
* look for @OneToMany, @ManyToMany and @ElementCollection annotations
|
||||||
*/
|
*/
|
||||||
public boolean isMappedCollection(CtField field) {
|
@Override
|
||||||
|
public boolean isMappedCollection(UnloadedField field) {
|
||||||
return field.hasAnnotation( OneToMany.class ) || field.hasAnnotation( ManyToMany.class ) || field.hasAnnotation( ElementCollection.class );
|
return field.hasAnnotation( OneToMany.class ) || field.hasAnnotation( ManyToMany.class ) || field.hasAnnotation( ElementCollection.class );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* keep the same order.
|
* keep the same order.
|
||||||
*/
|
*/
|
||||||
public CtField[] order(CtField[] persistentFields) {
|
@Override
|
||||||
|
public UnloadedField[] order(UnloadedField[] persistentFields) {
|
||||||
return persistentFields;
|
return persistentFields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.bytecode.enhance.spi;
|
package org.hibernate.bytecode.enhance.spi;
|
||||||
|
|
||||||
import javassist.CtClass;
|
|
||||||
import javassist.CtField;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The context for performing an enhancement. Enhancement can happen in any number of ways:<ul>
|
* The context for performing an enhancement. Enhancement can happen in any number of ways:<ul>
|
||||||
* <li>Build time, via Ant</li>
|
* <li>Build time, via Ant</li>
|
||||||
|
@ -22,7 +19,6 @@ import javassist.CtField;
|
||||||
* the enhancement is being performed.
|
* the enhancement is being performed.
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
* @todo Not sure its a great idea to expose Javassist classes this way. maybe wrap them in our own contracts?
|
|
||||||
*/
|
*/
|
||||||
public interface EnhancementContext {
|
public interface EnhancementContext {
|
||||||
/**
|
/**
|
||||||
|
@ -41,7 +37,7 @@ public interface EnhancementContext {
|
||||||
*
|
*
|
||||||
* @return {@code true} if the class is an entity; {@code false} otherwise.
|
* @return {@code true} if the class is an entity; {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isEntityClass(CtClass classDescriptor);
|
public boolean isEntityClass(UnloadedClass classDescriptor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does the given class name represent an embeddable/component class?
|
* Does the given class name represent an embeddable/component class?
|
||||||
|
@ -50,7 +46,7 @@ public interface EnhancementContext {
|
||||||
*
|
*
|
||||||
* @return {@code true} if the class is an embeddable/component; {@code false} otherwise.
|
* @return {@code true} if the class is an embeddable/component; {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isCompositeClass(CtClass classDescriptor);
|
public boolean isCompositeClass(UnloadedClass classDescriptor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does the given class name represent an MappedSuperclass class?
|
* Does the given class name represent an MappedSuperclass class?
|
||||||
|
@ -59,7 +55,7 @@ public interface EnhancementContext {
|
||||||
*
|
*
|
||||||
* @return {@code true} if the class is an mapped super class; {@code false} otherwise.
|
* @return {@code true} if the class is an mapped super class; {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isMappedSuperclassClass(CtClass classDescriptor);
|
public boolean isMappedSuperclassClass(UnloadedClass classDescriptor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should we manage association of bi-directional persistent attributes for this field?
|
* Should we manage association of bi-directional persistent attributes for this field?
|
||||||
|
@ -70,7 +66,7 @@ public interface EnhancementContext {
|
||||||
* the association is managed, i.e. the associations are automatically set; {@code false} indicates that
|
* the association is managed, i.e. the associations are automatically set; {@code false} indicates that
|
||||||
* the management is handled by the user.
|
* the management is handled by the user.
|
||||||
*/
|
*/
|
||||||
public boolean doBiDirectionalAssociationManagement(CtField field);
|
public boolean doBiDirectionalAssociationManagement(UnloadedField field);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should we in-line dirty checking for persistent attributes for this class?
|
* Should we in-line dirty checking for persistent attributes for this class?
|
||||||
|
@ -80,7 +76,7 @@ public interface EnhancementContext {
|
||||||
* @return {@code true} indicates that dirty checking should be in-lined within the entity; {@code false}
|
* @return {@code true} indicates that dirty checking should be in-lined within the entity; {@code false}
|
||||||
* indicates it should not. In-lined is more easily serializable and probably more performant.
|
* indicates it should not. In-lined is more easily serializable and probably more performant.
|
||||||
*/
|
*/
|
||||||
public boolean doDirtyCheckingInline(CtClass classDescriptor);
|
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should we enhance field access to entities from this class?
|
* Should we enhance field access to entities from this class?
|
||||||
|
@ -90,7 +86,7 @@ public interface EnhancementContext {
|
||||||
* @return {@code true} indicates that any direct access to fields of entities should be routed to the enhanced
|
* @return {@code true} indicates that any direct access to fields of entities should be routed to the enhanced
|
||||||
* getter / setter method.
|
* getter / setter method.
|
||||||
*/
|
*/
|
||||||
public boolean doExtendedEnhancement(CtClass classDescriptor);
|
public boolean doExtendedEnhancement(UnloadedClass classDescriptor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does the given class define any lazy loadable attributes?
|
* Does the given class define any lazy loadable attributes?
|
||||||
|
@ -99,7 +95,7 @@ public interface EnhancementContext {
|
||||||
*
|
*
|
||||||
* @return true/false
|
* @return true/false
|
||||||
*/
|
*/
|
||||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor);
|
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor);
|
||||||
|
|
||||||
// todo : may be better to invert these 2 such that the context is asked for an ordered list of persistent fields for an entity/composite
|
// todo : may be better to invert these 2 such that the context is asked for an ordered list of persistent fields for an entity/composite
|
||||||
|
|
||||||
|
@ -113,7 +109,7 @@ public interface EnhancementContext {
|
||||||
*
|
*
|
||||||
* @return {@code true} if the field is ; {@code false} otherwise.
|
* @return {@code true} if the field is ; {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isPersistentField(CtField ctField);
|
public boolean isPersistentField(UnloadedField ctField);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For fields which are persistent (according to {@link #isPersistentField}), determine the corresponding ordering
|
* For fields which are persistent (according to {@link #isPersistentField}), determine the corresponding ordering
|
||||||
|
@ -123,7 +119,7 @@ public interface EnhancementContext {
|
||||||
*
|
*
|
||||||
* @return The ordered references.
|
* @return The ordered references.
|
||||||
*/
|
*/
|
||||||
public CtField[] order(CtField[] persistentFields);
|
public UnloadedField[] order(UnloadedField[] persistentFields);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if a field is lazy loadable.
|
* Determine if a field is lazy loadable.
|
||||||
|
@ -132,12 +128,12 @@ public interface EnhancementContext {
|
||||||
*
|
*
|
||||||
* @return {@code true} if the field is lazy loadable; {@code false} otherwise.
|
* @return {@code true} if the field is lazy loadable; {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isLazyLoadable(CtField field);
|
public boolean isLazyLoadable(UnloadedField field);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param field the field to check
|
* @param field the field to check
|
||||||
*
|
*
|
||||||
* @return {@code true} if the field is mapped
|
* @return {@code true} if the field is mapped
|
||||||
*/
|
*/
|
||||||
public boolean isMappedCollection(CtField field);
|
public boolean isMappedCollection(UnloadedField field);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.bytecode.enhance.spi;
|
||||||
|
|
||||||
|
public class EnhancementContextWrapper implements EnhancementContext {
|
||||||
|
|
||||||
|
private final ClassLoader loadingClassloader;
|
||||||
|
private final EnhancementContext wrappedContext;
|
||||||
|
|
||||||
|
public EnhancementContextWrapper(EnhancementContext wrappedContext, ClassLoader loadingClassloader) {
|
||||||
|
this.wrappedContext = wrappedContext;
|
||||||
|
this.loadingClassloader = loadingClassloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLoader getLoadingClassLoader() {
|
||||||
|
return loadingClassloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEntityClass(UnloadedClass classDescriptor) {
|
||||||
|
return wrappedContext.isEntityClass( classDescriptor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompositeClass(UnloadedClass classDescriptor) {
|
||||||
|
return wrappedContext.isCompositeClass( classDescriptor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMappedSuperclassClass(UnloadedClass classDescriptor) {
|
||||||
|
return wrappedContext.isMappedSuperclassClass( classDescriptor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
|
||||||
|
return wrappedContext.doBiDirectionalAssociationManagement( field );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
|
||||||
|
return wrappedContext.doDirtyCheckingInline( classDescriptor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
|
||||||
|
return wrappedContext.doExtendedEnhancement( classDescriptor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
|
||||||
|
return wrappedContext.hasLazyLoadableAttributes( classDescriptor );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPersistentField(UnloadedField ctField) {
|
||||||
|
return wrappedContext.isPersistentField( ctField );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnloadedField[] order(UnloadedField[] persistentFields) {
|
||||||
|
return wrappedContext.order( persistentFields );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLazyLoadable(UnloadedField field) {
|
||||||
|
return wrappedContext.isLazyLoadable( field );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMappedCollection(UnloadedField field) {
|
||||||
|
return wrappedContext.isMappedCollection( field );
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,11 @@ import org.hibernate.HibernateException;
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class EnhancementException extends HibernateException {
|
public class EnhancementException extends HibernateException {
|
||||||
|
|
||||||
|
public EnhancementException(String message) {
|
||||||
|
super( message );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an EnhancementException
|
* Constructs an EnhancementException
|
||||||
*
|
*
|
||||||
|
|
|
@ -6,31 +6,8 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.bytecode.enhance.spi;
|
package org.hibernate.bytecode.enhance.spi;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.File;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import javassist.ClassPool;
|
|
||||||
import javassist.CtClass;
|
|
||||||
import javassist.LoaderClassPath;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.bytecode.enhance.internal.CompositeEnhancer;
|
|
||||||
import org.hibernate.bytecode.enhance.internal.EntityEnhancer;
|
|
||||||
import org.hibernate.bytecode.enhance.internal.FieldWriter;
|
|
||||||
import org.hibernate.bytecode.enhance.internal.MappedSuperclassEnhancer;
|
|
||||||
import org.hibernate.bytecode.enhance.internal.PersistentAttributesEnhancer;
|
|
||||||
import org.hibernate.bytecode.enhance.internal.PersistentAttributesHelper;
|
|
||||||
import org.hibernate.engine.spi.Managed;
|
|
||||||
import org.hibernate.engine.spi.ManagedComposite;
|
|
||||||
import org.hibernate.engine.spi.ManagedEntity;
|
|
||||||
import org.hibernate.engine.spi.ManagedMappedSuperclass;
|
|
||||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
|
||||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
|
||||||
import org.hibernate.internal.CoreLogging;
|
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class responsible for performing enhancement.
|
* Class responsible for performing enhancement.
|
||||||
|
@ -39,22 +16,7 @@ import org.hibernate.internal.CoreMessageLogger;
|
||||||
* @author Jason Greene
|
* @author Jason Greene
|
||||||
* @author Luis Barreiro
|
* @author Luis Barreiro
|
||||||
*/
|
*/
|
||||||
public class Enhancer {
|
public interface Enhancer {
|
||||||
private static final CoreMessageLogger log = CoreLogging.messageLogger( Enhancer.class );
|
|
||||||
|
|
||||||
protected final EnhancementContext 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 Enhancer(EnhancementContext enhancementContext) {
|
|
||||||
this.enhancementContext = enhancementContext;
|
|
||||||
this.classPool = buildClassPool( enhancementContext );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the enhancement.
|
* Performs the enhancement.
|
||||||
|
@ -62,140 +24,11 @@ public class Enhancer {
|
||||||
* @param className The name of the class whose bytecode is being enhanced.
|
* @param className The name of the class whose bytecode is being enhanced.
|
||||||
* @param originalBytes The class's original (pre-enhancement) byte code
|
* @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
|
* @return The enhanced bytecode. If the original bytes are not enhanced, null is returned.
|
||||||
* already enhanced or we could not enhance it for some reason.
|
|
||||||
*
|
*
|
||||||
* @throws EnhancementException Indicates a problem performing the enhancement
|
* @throws EnhancementException Indicates a problem performing the enhancement
|
||||||
*/
|
*/
|
||||||
public synchronized byte[] enhance(String className, byte[] originalBytes) throws EnhancementException {
|
byte[] enhance(String className, byte[] originalBytes) throws EnhancementException;
|
||||||
try {
|
|
||||||
final CtClass managedCtClass = classPool.makeClassIfNew( new ByteArrayInputStream( originalBytes ) );
|
|
||||||
enhance( managedCtClass );
|
|
||||||
return getByteCode( managedCtClass );
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
log.unableToBuildEnhancementMetamodel( className );
|
|
||||||
return originalBytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassPool buildClassPool(final EnhancementContext enhancementContext) {
|
byte[] enhance(File javaClassFile) throws EnhancementException, IOException;
|
||||||
final ClassPool classPool = new ClassPool( false ) {
|
|
||||||
@Override
|
|
||||||
public ClassLoader getClassLoader() {
|
|
||||||
return enhancementContext.getLoadingClassLoader();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
final 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 );
|
|
||||||
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 void enhance(CtClass managedCtClass) {
|
|
||||||
// can't effectively enhance interfaces
|
|
||||||
if ( managedCtClass.isInterface() ) {
|
|
||||||
log.debugf( "Skipping enhancement of [%s]: it's an interface!", managedCtClass.getName() );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// skip already enhanced classes
|
|
||||||
if ( alreadyEnhanced( managedCtClass ) ) {
|
|
||||||
log.debugf( "Skipping enhancement of [%s]: already enhanced", managedCtClass.getName() );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( enhancementContext.isEntityClass( managedCtClass ) ) {
|
|
||||||
log.infof( "Enhancing [%s] as Entity", managedCtClass.getName() );
|
|
||||||
new EntityEnhancer( enhancementContext ).enhance( managedCtClass );
|
|
||||||
}
|
|
||||||
else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
|
|
||||||
log.infof( "Enhancing [%s] as Composite", managedCtClass.getName() );
|
|
||||||
new CompositeEnhancer( enhancementContext ).enhance( managedCtClass );
|
|
||||||
}
|
|
||||||
else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
|
|
||||||
log.infof( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() );
|
|
||||||
new MappedSuperclassEnhancer( enhancementContext ).enhance( managedCtClass );
|
|
||||||
}
|
|
||||||
else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
|
|
||||||
log.infof( "Extended enhancement of [%s]", managedCtClass.getName() );
|
|
||||||
new PersistentAttributesEnhancer( enhancementContext ).extendedEnhancement( managedCtClass );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.debugf( "Skipping enhancement of [%s]: not entity or composite", managedCtClass.getName() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean alreadyEnhanced(CtClass managedCtClass) {
|
|
||||||
if ( !PersistentAttributesHelper.isAssignable( managedCtClass, Managed.class.getName() ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// HHH-10977 - When a mapped superclass gets enhanced before a subclassing entity, the entity does not get enhanced, but it implements the Managed interface
|
|
||||||
return enhancementContext.isEntityClass( managedCtClass ) && PersistentAttributesHelper.isAssignable( managedCtClass, ManagedEntity.class.getName() )
|
|
||||||
|| enhancementContext.isCompositeClass( managedCtClass ) && PersistentAttributesHelper.isAssignable( managedCtClass, ManagedComposite.class.getName() )
|
|
||||||
|| enhancementContext.isMappedSuperclassClass( managedCtClass ) && PersistentAttributesHelper.isAssignable( managedCtClass, ManagedMappedSuperclass.class.getName() );
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] getByteCode(CtClass managedCtClass) {
|
|
||||||
final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
|
||||||
final 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() );
|
|
||||||
}
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Should use enhance(String, byte[]) and a proper EnhancementContext
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public byte[] enhanceComposite(String className, byte[] originalBytes) throws EnhancementException {
|
|
||||||
return enhance( className, originalBytes );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.bytecode.enhance.spi;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
public interface UnloadedClass {
|
||||||
|
|
||||||
|
boolean hasAnnotation(Class<? extends Annotation> annotationType);
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.bytecode.enhance.spi;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
public interface UnloadedField {
|
||||||
|
|
||||||
|
boolean hasAnnotation(Class<? extends Annotation> annotationType);
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.hibernate.AssertionFailure;
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.bytecode.spi.BasicProxyFactory;
|
||||||
|
import org.hibernate.proxy.ProxyConfiguration;
|
||||||
|
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
|
import net.bytebuddy.dynamic.scaffold.TypeValidation;
|
||||||
|
import net.bytebuddy.implementation.FieldAccessor;
|
||||||
|
import net.bytebuddy.implementation.MethodDelegation;
|
||||||
|
import net.bytebuddy.implementation.auxiliary.AuxiliaryType;
|
||||||
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
|
public class BasicProxyFactoryImpl implements BasicProxyFactory {
|
||||||
|
|
||||||
|
private static final ConcurrentMap<Set<Class>, Class> CACHE = new ConcurrentHashMap<Set<Class>, Class>();
|
||||||
|
|
||||||
|
private static final Class[] NO_INTERFACES = new Class[0];
|
||||||
|
|
||||||
|
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" );
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Class> key = new HashSet<Class>();
|
||||||
|
if ( superClass != null ) {
|
||||||
|
key.add( superClass );
|
||||||
|
}
|
||||||
|
if ( interfaces != null && interfaces.length > 0 ) {
|
||||||
|
key.addAll( Arrays.asList( interfaces ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
Class proxyClass = CACHE.get( key );
|
||||||
|
|
||||||
|
if ( proxyClass == null ) {
|
||||||
|
proxyClass = new ByteBuddy()
|
||||||
|
.with( TypeValidation.DISABLED )
|
||||||
|
.with( new AuxiliaryType.NamingStrategy.SuffixingRandom( "HibernateBasicProxy" ) )
|
||||||
|
.subclass( superClass == null ? Object.class : superClass )
|
||||||
|
.implement( interfaces == null ? NO_INTERFACES : interfaces )
|
||||||
|
.defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE )
|
||||||
|
.method( ElementMatchers.isVirtual().and( ElementMatchers.not( ElementMatchers.isFinalizer() ) ) )
|
||||||
|
.intercept( MethodDelegation.toField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME ) )
|
||||||
|
.implement( ProxyConfiguration.class )
|
||||||
|
.intercept( FieldAccessor.ofField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME ).withAssigner( Assigner.DEFAULT, Assigner.Typing.DYNAMIC ) )
|
||||||
|
.make()
|
||||||
|
.load( BasicProxyFactory.class.getClassLoader() )
|
||||||
|
.getLoaded();
|
||||||
|
Class previousProxy = CACHE.putIfAbsent( key, proxyClass );
|
||||||
|
if ( previousProxy != null ) {
|
||||||
|
proxyClass = previousProxy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.proxyClass = proxyClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getProxy() {
|
||||||
|
try {
|
||||||
|
final ProxyConfiguration proxy = (ProxyConfiguration) proxyClass.newInstance();
|
||||||
|
proxy.$$_hibernate_set_interceptor( new PassThroughInterceptor( proxy, proxyClass.getName() ) );
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
throw new HibernateException( "Unable to instantiate proxy instance" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInstance(Object object) {
|
||||||
|
return proxyClass.isInstance( object );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.bytecode.internal.bytebuddy;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
|
||||||
|
public class BulkAccessorException extends HibernateException {
|
||||||
|
private final int index;
|
||||||
|
|
||||||
|
public BulkAccessorException(String message) {
|
||||||
|
this( message, -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public BulkAccessorException(String message, int index) {
|
||||||
|
this( message, index, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
public BulkAccessorException(String message, Exception cause) {
|
||||||
|
this( message, -1, cause );
|
||||||
|
}
|
||||||
|
|
||||||
|
public BulkAccessorException(String message, int index, Exception cause) {
|
||||||
|
super( message + " : @" + index, cause );
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndex() {
|
||||||
|
return this.index;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,285 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.bytecode.enhance.internal.bytebuddy.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 net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.NamingStrategy;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.scaffold.TypeValidation;
|
||||||
|
import net.bytebuddy.implementation.Implementation;
|
||||||
|
import net.bytebuddy.implementation.MethodCall;
|
||||||
|
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
|
||||||
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
|
import net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveBoxingDelegate;
|
||||||
|
import net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveUnboxingDelegate;
|
||||||
|
import net.bytebuddy.implementation.bytecode.assign.reference.ReferenceTypeAwareAssigner;
|
||||||
|
import net.bytebuddy.jar.asm.MethodVisitor;
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
|
import net.bytebuddy.jar.asm.Type;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
|
public class BytecodeProviderImpl implements BytecodeProvider {
|
||||||
|
|
||||||
|
private final ConcurrentMap<Class, Class> FAST_CLASSES = new ConcurrentHashMap<Class, Class>();
|
||||||
|
private final ConcurrentMap<Class, Class> BULK_ACCESSORS = new ConcurrentHashMap<Class, Class>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProxyFactoryFactory getProxyFactoryFactory() {
|
||||||
|
return new ProxyFactoryFactoryImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReflectionOptimizer getReflectionOptimizer(
|
||||||
|
final Class clazz,
|
||||||
|
final String[] getterNames,
|
||||||
|
final String[] setterNames,
|
||||||
|
final Class[] types) {
|
||||||
|
final Method[] getters = new Method[getterNames.length];
|
||||||
|
final Method[] setters = new Method[setterNames.length];
|
||||||
|
findAccessors( clazz, getterNames, setterNames, types, getters, setters );
|
||||||
|
final Constructor<?> constructor = findConstructor( clazz );
|
||||||
|
|
||||||
|
Class fastClass = FAST_CLASSES.get( clazz );
|
||||||
|
|
||||||
|
if ( fastClass == null ) {
|
||||||
|
fastClass = new ByteBuddy()
|
||||||
|
.with( TypeValidation.DISABLED )
|
||||||
|
.with( new NamingStrategy.SuffixingRandom( "HibernateInstantiator" ) )
|
||||||
|
.subclass( ReflectionOptimizer.InstantiationOptimizer.class )
|
||||||
|
.method( ElementMatchers.named( "newInstance" ) )
|
||||||
|
.intercept( MethodCall.construct( constructor ) )
|
||||||
|
.make()
|
||||||
|
.load( clazz.getClassLoader() )
|
||||||
|
.getLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
Class previousFastClass = FAST_CLASSES.putIfAbsent( clazz, fastClass );
|
||||||
|
if ( previousFastClass != null ) {
|
||||||
|
fastClass = previousFastClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class bulkAccessor = BULK_ACCESSORS.get( clazz );
|
||||||
|
|
||||||
|
if ( bulkAccessor == null ) {
|
||||||
|
bulkAccessor = new ByteBuddy()
|
||||||
|
.with( TypeValidation.DISABLED )
|
||||||
|
.with( new NamingStrategy.SuffixingRandom( "HibernateAccessOptimizer" ) )
|
||||||
|
.subclass( ReflectionOptimizer.AccessOptimizer.class )
|
||||||
|
.method( ElementMatchers.named( "getPropertyValues" ) )
|
||||||
|
.intercept( new Implementation.Simple( new GetPropertyValues( clazz, getters ) ) )
|
||||||
|
.method( ElementMatchers.named( "setPropertyValues" ) )
|
||||||
|
.intercept( new Implementation.Simple( new SetPropertyValues( clazz, setters ) ) )
|
||||||
|
.method( ElementMatchers.named( "getPropertyNames" ) )
|
||||||
|
.intercept( MethodCall.call( new CloningPropertyCall( getterNames ) ) )
|
||||||
|
.make()
|
||||||
|
.load( clazz.getClassLoader() )
|
||||||
|
.getLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Class previousBulkAccessor = BULK_ACCESSORS.putIfAbsent( clazz, fastClass );
|
||||||
|
if ( previousBulkAccessor != null ) {
|
||||||
|
bulkAccessor = previousBulkAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new ReflectionOptimizerImpl(
|
||||||
|
(ReflectionOptimizer.InstantiationOptimizer) fastClass.newInstance(),
|
||||||
|
(ReflectionOptimizer.AccessOptimizer) bulkAccessor.newInstance()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (Exception exception) {
|
||||||
|
throw new HibernateException( exception );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GetPropertyValues implements ByteCodeAppender {
|
||||||
|
|
||||||
|
private final Class clazz;
|
||||||
|
|
||||||
|
private final Method[] getters;
|
||||||
|
|
||||||
|
public GetPropertyValues(Class clazz, Method[] getters) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
this.getters = getters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size apply(
|
||||||
|
MethodVisitor methodVisitor,
|
||||||
|
Implementation.Context implementationContext,
|
||||||
|
MethodDescription instrumentedMethod) {
|
||||||
|
methodVisitor.visitLdcInsn( getters.length );
|
||||||
|
methodVisitor.visitTypeInsn( Opcodes.ANEWARRAY, Type.getInternalName( Object.class ) );
|
||||||
|
int index = 0;
|
||||||
|
for ( Method getter : getters ) {
|
||||||
|
methodVisitor.visitInsn( Opcodes.DUP );
|
||||||
|
methodVisitor.visitLdcInsn( index++ );
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 );
|
||||||
|
methodVisitor.visitTypeInsn( Opcodes.CHECKCAST, Type.getInternalName( clazz ) );
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
Type.getInternalName( clazz ),
|
||||||
|
getter.getName(),
|
||||||
|
Type.getMethodDescriptor( getter ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
if ( getter.getReturnType().isPrimitive() ) {
|
||||||
|
PrimitiveBoxingDelegate.forPrimitive( new TypeDescription.ForLoadedType( getter.getReturnType() ) )
|
||||||
|
.assignBoxedTo(
|
||||||
|
TypeDescription.Generic.OBJECT,
|
||||||
|
ReferenceTypeAwareAssigner.INSTANCE,
|
||||||
|
Assigner.Typing.STATIC
|
||||||
|
)
|
||||||
|
.apply( methodVisitor, implementationContext );
|
||||||
|
}
|
||||||
|
methodVisitor.visitInsn( Opcodes.AASTORE );
|
||||||
|
}
|
||||||
|
methodVisitor.visitInsn( Opcodes.ARETURN );
|
||||||
|
return new Size( 6, instrumentedMethod.getStackSize() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SetPropertyValues implements ByteCodeAppender {
|
||||||
|
|
||||||
|
private final Class clazz;
|
||||||
|
|
||||||
|
private final Method[] setters;
|
||||||
|
|
||||||
|
public SetPropertyValues(Class clazz, Method[] setters) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
this.setters = setters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size apply(
|
||||||
|
MethodVisitor methodVisitor,
|
||||||
|
Implementation.Context implementationContext,
|
||||||
|
MethodDescription instrumentedMethod) {
|
||||||
|
int index = 0;
|
||||||
|
for ( Method setter : setters ) {
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 1 );
|
||||||
|
methodVisitor.visitTypeInsn( Opcodes.CHECKCAST, Type.getInternalName( clazz ) );
|
||||||
|
methodVisitor.visitVarInsn( Opcodes.ALOAD, 2 );
|
||||||
|
methodVisitor.visitLdcInsn( index++ );
|
||||||
|
methodVisitor.visitInsn( Opcodes.AALOAD );
|
||||||
|
if ( setter.getParameterTypes()[0].isPrimitive() ) {
|
||||||
|
PrimitiveUnboxingDelegate.forReferenceType( TypeDescription.Generic.OBJECT )
|
||||||
|
.assignUnboxedTo(
|
||||||
|
new TypeDescription.Generic.OfNonGenericType.ForLoadedType( setter.getParameterTypes()[0] ),
|
||||||
|
ReferenceTypeAwareAssigner.INSTANCE,
|
||||||
|
Assigner.Typing.DYNAMIC
|
||||||
|
)
|
||||||
|
.apply( methodVisitor, implementationContext );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
methodVisitor.visitTypeInsn( Opcodes.CHECKCAST, Type.getInternalName( setter.getParameterTypes()[0] ) );
|
||||||
|
}
|
||||||
|
methodVisitor.visitMethodInsn(
|
||||||
|
Opcodes.INVOKEVIRTUAL,
|
||||||
|
Type.getInternalName( clazz ),
|
||||||
|
setter.getName(),
|
||||||
|
Type.getMethodDescriptor( setter ),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
methodVisitor.visitInsn( Opcodes.RETURN );
|
||||||
|
return new Size( 4, instrumentedMethod.getStackSize() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Constructor<?> findConstructor(Class clazz) {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredConstructor();
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodException e) {
|
||||||
|
throw new HibernateException( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CloningPropertyCall implements Callable<String[]> {
|
||||||
|
|
||||||
|
private final String[] propertyNames;
|
||||||
|
|
||||||
|
private CloningPropertyCall(String[] propertyNames) {
|
||||||
|
this.propertyNames = propertyNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] call() {
|
||||||
|
return propertyNames.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enhancer getEnhancer(EnhancementContext enhancementContext) {
|
||||||
|
return new EnhancerImpl( enhancementContext );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.bytecode.internal.bytebuddy;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.hibernate.proxy.ProxyConfiguration;
|
||||||
|
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.AllArguments;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.Origin;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.This;
|
||||||
|
|
||||||
|
public class PassThroughInterceptor implements ProxyConfiguration.Interceptor {
|
||||||
|
|
||||||
|
private HashMap data = new HashMap();
|
||||||
|
private final Object proxiedObject;
|
||||||
|
private final String proxiedClassName;
|
||||||
|
|
||||||
|
public PassThroughInterceptor(Object proxiedObject, String proxiedClassName) {
|
||||||
|
this.proxiedObject = proxiedObject;
|
||||||
|
this.proxiedClassName = proxiedClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public Object intercept(Object instance, Method method, Object[] arguments) throws Exception {
|
||||||
|
final String name = method.getName();
|
||||||
|
if ( "toString".equals( name ) ) {
|
||||||
|
return proxiedClassName + "@" + System.identityHashCode( instance );
|
||||||
|
}
|
||||||
|
else if ( "equals".equals( name ) ) {
|
||||||
|
return proxiedObject == instance;
|
||||||
|
}
|
||||||
|
else if ( "hashCode".equals( name ) ) {
|
||||||
|
return System.identityHashCode( instance );
|
||||||
|
}
|
||||||
|
|
||||||
|
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, arguments[0] );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// todo : what else to do here?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
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.bytebuddy.ByteBuddyProxyFactory;
|
||||||
|
|
||||||
|
public class ProxyFactoryFactoryImpl implements ProxyFactoryFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProxyFactory buildProxyFactory(SessionFactoryImplementor sessionFactory) {
|
||||||
|
return new ByteBuddyProxyFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BasicProxyFactory buildBasicProxyFactory(Class superClass, Class[] interfaces) {
|
||||||
|
return new BasicProxyFactoryImpl( superClass, interfaces );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.bytecode.internal.bytebuddy;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.spi.ReflectionOptimizer;
|
||||||
|
|
||||||
|
public class ReflectionOptimizerImpl implements ReflectionOptimizer, Serializable {
|
||||||
|
private final InstantiationOptimizer instantiationOptimizer;
|
||||||
|
private final AccessOptimizer accessOptimizer;
|
||||||
|
|
||||||
|
public ReflectionOptimizerImpl(
|
||||||
|
InstantiationOptimizer instantiationOptimizer,
|
||||||
|
AccessOptimizer accessOptimizer) {
|
||||||
|
this.instantiationOptimizer = instantiationOptimizer;
|
||||||
|
this.accessOptimizer = accessOptimizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstantiationOptimizer getInstantiationOptimizer() {
|
||||||
|
return instantiationOptimizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessOptimizer getAccessOptimizer() {
|
||||||
|
return accessOptimizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Byte Buddy support internals
|
||||||
|
*/
|
||||||
|
package org.hibernate.bytecode.internal.bytebuddy;
|
|
@ -8,6 +8,9 @@ package org.hibernate.bytecode.internal.javassist;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
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.BytecodeProvider;
|
||||||
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
|
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
|
||||||
import org.hibernate.bytecode.spi.ReflectionOptimizer;
|
import org.hibernate.bytecode.spi.ReflectionOptimizer;
|
||||||
|
@ -92,4 +95,8 @@ public class BytecodeProviderImpl implements BytecodeProvider {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enhancer getEnhancer(EnhancementContext enhancementContext) {
|
||||||
|
return new EnhancerImpl( enhancementContext );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.bytecode.spi;
|
package org.hibernate.bytecode.spi;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contract for providers of bytecode services to Hibernate.
|
* Contract for providers of bytecode services to Hibernate.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -36,4 +39,13 @@ public interface BytecodeProvider {
|
||||||
* @return The reflection optimization delegate.
|
* @return The reflection optimization delegate.
|
||||||
*/
|
*/
|
||||||
ReflectionOptimizer getReflectionOptimizer(Class clazz, String[] getterNames, String[] setterNames, Class[] types);
|
ReflectionOptimizer getReflectionOptimizer(Class clazz, String[] getterNames, String[] setterNames, Class[] types);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a byte code enhancer that implements the enhancements described in the supplied enhancement context.
|
||||||
|
*
|
||||||
|
* @param enhancementContext The enhancement context that describes the enhancements to apply.
|
||||||
|
*
|
||||||
|
* @return An enhancer to perform byte code manipulations.
|
||||||
|
*/
|
||||||
|
Enhancer getEnhancer(EnhancementContext enhancementContext);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class InstrumentedClassLoader extends ClassLoader {
|
||||||
try {
|
try {
|
||||||
final byte[] originalBytecode = ByteCodeHelper.readByteCode( is );
|
final byte[] originalBytecode = ByteCodeHelper.readByteCode( is );
|
||||||
final byte[] transformedBytecode = classTransformer.transform( getParent(), name, null, null, originalBytecode );
|
final byte[] transformedBytecode = classTransformer.transform( getParent(), name, null, null, originalBytecode );
|
||||||
if ( originalBytecode == transformedBytecode ) {
|
if ( transformedBytecode == null ) {
|
||||||
// no transformations took place, so handle it as we would a
|
// no transformations took place, so handle it as we would a
|
||||||
// non-instrumented class
|
// non-instrumented class
|
||||||
return getParent().loadClass( name );
|
return getParent().loadClass( name );
|
||||||
|
@ -57,7 +57,7 @@ public class InstrumentedClassLoader extends ClassLoader {
|
||||||
return defineClass( name, transformedBytecode, 0, transformedBytecode.length );
|
return defineClass( name, transformedBytecode, 0, transformedBytecode.length );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch( Throwable t ) {
|
catch ( Throwable t ) {
|
||||||
throw new ClassNotFoundException( name + " not found", t );
|
throw new ClassNotFoundException( name + " not found", t );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -314,17 +314,20 @@ public final class Environment implements AvailableSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BytecodeProvider buildBytecodeProvider(Properties properties) {
|
public static BytecodeProvider buildBytecodeProvider(Properties properties) {
|
||||||
String provider = ConfigurationHelper.getString( BYTECODE_PROVIDER, properties, "javassist" );
|
String provider = ConfigurationHelper.getString( BYTECODE_PROVIDER, properties, "bytebuddy" );
|
||||||
LOG.bytecodeProvider( provider );
|
LOG.bytecodeProvider( provider );
|
||||||
return buildBytecodeProvider( provider );
|
return buildBytecodeProvider( provider );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BytecodeProvider buildBytecodeProvider(String providerName) {
|
private static BytecodeProvider buildBytecodeProvider(String providerName) {
|
||||||
|
if ( "bytebuddy".equals( providerName ) ) {
|
||||||
|
return new org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl();
|
||||||
|
}
|
||||||
if ( "javassist".equals( providerName ) ) {
|
if ( "javassist".equals( providerName ) ) {
|
||||||
return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
|
return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.unknownBytecodeProvider( providerName );
|
LOG.unknownBytecodeProvider( providerName );
|
||||||
return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
|
return new org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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.engine.spi;
|
||||||
|
|
||||||
|
import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
|
||||||
|
import org.hibernate.engine.spi.SelfDirtinessTracker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A self dirtiness tracker that declares additional methods that are intended for internal communication. This
|
||||||
|
* interface can be implemented optionally instead of the plain {@link SelfDirtinessTracker}.
|
||||||
|
*/
|
||||||
|
public interface ExtendedSelfDirtinessTracker extends SelfDirtinessTracker {
|
||||||
|
|
||||||
|
String REMOVE_DIRTY_FIELDS_NAME = "$$_hibernate_removeDirtyFields";
|
||||||
|
|
||||||
|
void $$_hibernate_getCollectionFieldDirtyNames(DirtyTracker dirtyTracker);
|
||||||
|
|
||||||
|
boolean $$_hibernate_areCollectionFieldsDirty();
|
||||||
|
|
||||||
|
void $$_hibernate_clearDirtyCollectionNames();
|
||||||
|
|
||||||
|
void $$_hibernate_removeDirtyFields(LazyAttributeLoadingInterceptor lazyInterceptor);
|
||||||
|
}
|
|
@ -467,8 +467,8 @@ public interface CoreMessageLogger extends BasicLogger {
|
||||||
String old,
|
String old,
|
||||||
String name);
|
String name);
|
||||||
|
|
||||||
@Message(value = "Javassist Enhancement failed: %s", id = 142)
|
@Message(value = "Bytecode enhancement failed: %s", id = 142)
|
||||||
String javassistEnhancementFailed(String entityName);
|
String bytecodeEnhancementFailed(String entityName);
|
||||||
|
|
||||||
@LogMessage(level = WARN)
|
@LogMessage(level = WARN)
|
||||||
@Message(value = "%s = false breaks the EJB3 specification", id = 144)
|
@Message(value = "%s = false breaks the EJB3 specification", id = 144)
|
||||||
|
@ -1327,7 +1327,7 @@ public interface CoreMessageLogger extends BasicLogger {
|
||||||
void unexpectedRowCounts();
|
void unexpectedRowCounts();
|
||||||
|
|
||||||
@LogMessage(level = WARN)
|
@LogMessage(level = WARN)
|
||||||
@Message(value = "unrecognized bytecode provider [%s], using javassist by default", id = 382)
|
@Message(value = "unrecognized bytecode provider [%s], using 'bytebuddy' by default", id = 382)
|
||||||
void unknownBytecodeProvider(String providerName);
|
void unknownBytecodeProvider(String providerName);
|
||||||
|
|
||||||
@LogMessage(level = WARN)
|
@LogMessage(level = WARN)
|
||||||
|
|
|
@ -51,6 +51,8 @@ import org.hibernate.boot.spi.MetadataImplementor;
|
||||||
import org.hibernate.boot.spi.SessionFactoryBuilderImplementor;
|
import org.hibernate.boot.spi.SessionFactoryBuilderImplementor;
|
||||||
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedField;
|
||||||
import org.hibernate.cfg.AttributeConverterDefinition;
|
import org.hibernate.cfg.AttributeConverterDefinition;
|
||||||
import org.hibernate.cfg.Environment;
|
import org.hibernate.cfg.Environment;
|
||||||
import org.hibernate.cfg.beanvalidation.BeanValidationIntegrator;
|
import org.hibernate.cfg.beanvalidation.BeanValidationIntegrator;
|
||||||
|
@ -276,39 +278,39 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
||||||
return new DefaultEnhancementContext() {
|
return new DefaultEnhancementContext() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEntityClass(CtClass classDescriptor) {
|
public boolean isEntityClass(UnloadedClass classDescriptor) {
|
||||||
return managedResources.getAnnotatedClassNames().contains( classDescriptor.getName() )
|
return managedResources.getAnnotatedClassNames().contains( classDescriptor.getName() )
|
||||||
&& super.isEntityClass( classDescriptor );
|
&& super.isEntityClass( classDescriptor );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompositeClass(CtClass classDescriptor) {
|
public boolean isCompositeClass(UnloadedClass classDescriptor) {
|
||||||
return managedResources.getAnnotatedClassNames().contains( classDescriptor.getName() )
|
return managedResources.getAnnotatedClassNames().contains( classDescriptor.getName() )
|
||||||
&& super.isCompositeClass( classDescriptor );
|
&& super.isCompositeClass( classDescriptor );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doBiDirectionalAssociationManagement(CtField field) {
|
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
|
||||||
return associationManagementEnabled;
|
return associationManagementEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
|
||||||
return dirtyTrackingEnabled;
|
return dirtyTrackingEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
|
||||||
return lazyInitializationEnabled;
|
return lazyInitializationEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLazyLoadable(CtField field) {
|
public boolean isLazyLoadable(UnloadedField field) {
|
||||||
return lazyInitializationEnabled;
|
return lazyInitializationEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doExtendedEnhancement(CtClass classDescriptor) {
|
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
|
||||||
// doesn't make any sense to have extended enhancement enabled at runtime. we only enhance entities anyway.
|
// doesn't make any sense to have extended enhancement enabled at runtime. we only enhance entities anyway.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,11 @@ package org.hibernate.jpa.internal.enhance;
|
||||||
import java.lang.instrument.IllegalClassFormatException;
|
import java.lang.instrument.IllegalClassFormatException;
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
|
|
||||||
import javassist.CtClass;
|
|
||||||
import javassist.CtField;
|
|
||||||
|
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.EnhancementContextWrapper;
|
||||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||||
import org.hibernate.bytecode.spi.ClassTransformer;
|
import org.hibernate.bytecode.spi.ClassTransformer;
|
||||||
|
import org.hibernate.cfg.Environment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -41,7 +40,7 @@ public class EnhancingClassTransformerImpl implements ClassTransformer {
|
||||||
// It also assumed that all calls come from the same class loader, which is fair, but this makes it more robust.
|
// It also assumed that all calls come from the same class loader, which is fair, but this makes it more robust.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Enhancer enhancer = new Enhancer( new EnhancementContextWrapper( enhancementContext, loader ) );
|
Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( new EnhancementContextWrapper( enhancementContext, loader ) );
|
||||||
return enhancer.enhance( className, classfileBuffer );
|
return enhancer.enhance( className, classfileBuffer );
|
||||||
}
|
}
|
||||||
catch (final Exception e) {
|
catch (final Exception e) {
|
||||||
|
@ -54,76 +53,4 @@ public class EnhancingClassTransformerImpl implements ClassTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper for a EnhancementContext that allows to set the right classloader.
|
|
||||||
private class EnhancementContextWrapper implements EnhancementContext {
|
|
||||||
|
|
||||||
private final ClassLoader loadingClassloader;
|
|
||||||
private final EnhancementContext wrappedContext;
|
|
||||||
|
|
||||||
private EnhancementContextWrapper(EnhancementContext wrappedContext, ClassLoader loadingClassloader) {
|
|
||||||
this.wrappedContext = wrappedContext;
|
|
||||||
this.loadingClassloader = loadingClassloader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClassLoader getLoadingClassLoader() {
|
|
||||||
return loadingClassloader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEntityClass(CtClass classDescriptor) {
|
|
||||||
return wrappedContext.isEntityClass( classDescriptor );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCompositeClass(CtClass classDescriptor) {
|
|
||||||
return wrappedContext.isCompositeClass( classDescriptor );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMappedSuperclassClass(CtClass classDescriptor) {
|
|
||||||
return wrappedContext.isMappedSuperclassClass( classDescriptor );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean doBiDirectionalAssociationManagement(CtField field) {
|
|
||||||
return wrappedContext.doBiDirectionalAssociationManagement( field );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
|
||||||
return wrappedContext.doDirtyCheckingInline( classDescriptor );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean doExtendedEnhancement(CtClass classDescriptor) {
|
|
||||||
return wrappedContext.doExtendedEnhancement( classDescriptor );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
|
||||||
return wrappedContext.hasLazyLoadableAttributes( classDescriptor );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPersistentField(CtField ctField) {
|
|
||||||
return wrappedContext.isPersistentField( ctField );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CtField[] order(CtField[] persistentFields) {
|
|
||||||
return wrappedContext.order( persistentFields );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLazyLoadable(CtField field) {
|
|
||||||
return wrappedContext.isLazyLoadable( field );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMappedCollection(CtField field) {
|
|
||||||
return wrappedContext.isMappedCollection( field );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.AllArguments;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.FieldValue;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.Origin;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.StubValue;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.This;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A proxy configuration allows the definition of an interceptor object that decides on the behavior of a proxy.
|
||||||
|
* This interface is meant for internal use but is in a public package in order to provide code generation.
|
||||||
|
* <p>
|
||||||
|
* While this interface depends on Byte Buddy types, this is only true for annotation types which are silently
|
||||||
|
* suppressed by the runtime if they are not available on a class loader. This allows using this interceptor
|
||||||
|
* and configuration with for example OSGi without any export of Byte Buddy when using Hibernate.
|
||||||
|
*/
|
||||||
|
public interface ProxyConfiguration {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The canonical field name for an interceptor object stored in a proxied object.
|
||||||
|
*/
|
||||||
|
String INTERCEPTOR_FIELD_NAME = "$$_hibernate_interceptor";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines an interceptor object that specifies the behavior of the proxy object.
|
||||||
|
*
|
||||||
|
* @param interceptor The interceptor object.
|
||||||
|
*/
|
||||||
|
void $$_hibernate_set_interceptor(Interceptor interceptor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interceptor object that is responsible for invoking a proxy's method.
|
||||||
|
*/
|
||||||
|
interface Interceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercepts a method call to a proxy.
|
||||||
|
*
|
||||||
|
* @param instance The proxied instance.
|
||||||
|
* @param method The invoked method.
|
||||||
|
* @param arguments The intercepted method arguments.
|
||||||
|
*
|
||||||
|
* @return The method's return value.
|
||||||
|
*
|
||||||
|
* @throws Throwable If the intercepted method raises an exception.
|
||||||
|
*/
|
||||||
|
@RuntimeType
|
||||||
|
Object intercept(@This Object instance, @Origin Method method, @AllArguments Object[] arguments) throws Throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An static interceptor that guards against method calls before the interceptor is set.
|
||||||
|
*/
|
||||||
|
class InterceptorDispatcher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercepts a method call to a proxy.
|
||||||
|
*
|
||||||
|
* @param instance The proxied instance.
|
||||||
|
* @param method The invoked method.
|
||||||
|
* @param arguments The method arguments.
|
||||||
|
* @param stubValue The intercepted method's default value.
|
||||||
|
* @param interceptor The proxy object's interceptor instance.
|
||||||
|
*
|
||||||
|
* @return The intercepted method's return value.
|
||||||
|
*
|
||||||
|
* @throws Throwable If the intercepted method raises an exception.
|
||||||
|
*/
|
||||||
|
@RuntimeType
|
||||||
|
public static Object intercept(
|
||||||
|
@This final Object instance,
|
||||||
|
@Origin final Method method,
|
||||||
|
@AllArguments final Object[] arguments,
|
||||||
|
@StubValue final Object stubValue,
|
||||||
|
@FieldValue(INTERCEPTOR_FIELD_NAME) Interceptor interceptor
|
||||||
|
) throws Throwable {
|
||||||
|
if ( interceptor == null ) {
|
||||||
|
if ( method.getName().equals( "getHibernateLazyInitializer" ) ) {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return stubValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return interceptor.intercept( instance, method, arguments );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
import org.hibernate.internal.util.ReflectHelper;
|
||||||
|
import org.hibernate.proxy.ProxyConfiguration;
|
||||||
|
import org.hibernate.proxy.pojo.BasicLazyInitializer;
|
||||||
|
import org.hibernate.type.CompositeType;
|
||||||
|
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.AllArguments;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.FieldValue;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.Origin;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.StubValue;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.This;
|
||||||
|
|
||||||
|
import static org.hibernate.internal.CoreLogging.messageLogger;
|
||||||
|
|
||||||
|
public class ByteBuddyInterceptor extends BasicLazyInitializer implements ProxyConfiguration.Interceptor {
|
||||||
|
private static final CoreMessageLogger LOG = messageLogger( ByteBuddyInterceptor.class );
|
||||||
|
|
||||||
|
private final Class[] interfaces;
|
||||||
|
|
||||||
|
public ByteBuddyInterceptor(
|
||||||
|
String entityName,
|
||||||
|
Class persistentClass,
|
||||||
|
Class[] interfaces,
|
||||||
|
Serializable id,
|
||||||
|
Method getIdentifierMethod,
|
||||||
|
Method setIdentifierMethod,
|
||||||
|
CompositeType componentIdType,
|
||||||
|
SharedSessionContractImplementor session,
|
||||||
|
boolean overridesEquals) {
|
||||||
|
super( entityName, persistentClass, id, getIdentifierMethod, setIdentifierMethod, componentIdType, session, overridesEquals );
|
||||||
|
this.interfaces = interfaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object intercept(Object proxy, Method thisMethod, Object[] args) throws Throwable {
|
||||||
|
Object result;
|
||||||
|
try {
|
||||||
|
result = this.invoke( thisMethod, args, proxy );
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
throw 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object serializableProxy() {
|
||||||
|
return new SerializableProxy(
|
||||||
|
getEntityName(),
|
||||||
|
persistentClass,
|
||||||
|
interfaces,
|
||||||
|
getIdentifier(),
|
||||||
|
( isReadOnlySettingAvailable() ? Boolean.valueOf( isReadOnly() ) : isReadOnlyBeforeAttachedToSession() ),
|
||||||
|
getIdentifierMethod,
|
||||||
|
setIdentifierMethod,
|
||||||
|
componentIdType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,229 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
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.proxy.ProxyConfiguration;
|
||||||
|
import org.hibernate.type.CompositeType;
|
||||||
|
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.NamingStrategy;
|
||||||
|
import net.bytebuddy.description.modifier.Visibility;
|
||||||
|
import net.bytebuddy.dynamic.scaffold.TypeValidation;
|
||||||
|
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
|
||||||
|
import net.bytebuddy.implementation.FieldAccessor;
|
||||||
|
import net.bytebuddy.implementation.FixedValue;
|
||||||
|
import net.bytebuddy.implementation.MethodDelegation;
|
||||||
|
import net.bytebuddy.implementation.SuperMethodCall;
|
||||||
|
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static org.hibernate.internal.CoreLogging.messageLogger;
|
||||||
|
|
||||||
|
public class ByteBuddyProxyFactory implements ProxyFactory, Serializable {
|
||||||
|
private static final CoreMessageLogger LOG = messageLogger( ByteBuddyProxyFactory.class );
|
||||||
|
|
||||||
|
private static final ConcurrentMap<Set<Class>, Class> CACHE = new ConcurrentHashMap<Set<Class>, Class>();
|
||||||
|
|
||||||
|
private Class persistentClass;
|
||||||
|
private String entityName;
|
||||||
|
private Class[] interfaces;
|
||||||
|
private Method getIdentifierMethod;
|
||||||
|
private Method setIdentifierMethod;
|
||||||
|
private CompositeType componentIdType;
|
||||||
|
private boolean overridesEquals;
|
||||||
|
|
||||||
|
private Class proxyClass;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInstantiate(
|
||||||
|
String entityName,
|
||||||
|
Class persistentClass,
|
||||||
|
Set<Class> interfaces,
|
||||||
|
Method getIdentifierMethod,
|
||||||
|
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 = buildProxy( persistentClass, this.interfaces );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class[] toArray(Set<Class> interfaces) {
|
||||||
|
if ( interfaces == null ) {
|
||||||
|
return ArrayHelper.EMPTY_CLASS_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return interfaces.toArray( new Class[interfaces.size()] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class buildProxy(
|
||||||
|
final Class persistentClass,
|
||||||
|
final Class[] interfaces) {
|
||||||
|
Set<Class> key = new HashSet<Class>();
|
||||||
|
if ( interfaces.length == 1 ) {
|
||||||
|
key.add( persistentClass );
|
||||||
|
}
|
||||||
|
key.addAll( Arrays.asList( interfaces ) );
|
||||||
|
|
||||||
|
Class<?> proxy = CACHE.get( key );
|
||||||
|
if ( proxy != null ) {
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy = new ByteBuddy()
|
||||||
|
.with( TypeValidation.DISABLED )
|
||||||
|
.with( new NamingStrategy.SuffixingRandom( "HibernateProxy" ) )
|
||||||
|
.subclass( interfaces.length == 1 ? persistentClass : Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING )
|
||||||
|
.implement( (Type[]) interfaces )
|
||||||
|
.method( ElementMatchers.isVirtual().and( ElementMatchers.not( ElementMatchers.isFinalizer() ) ) )
|
||||||
|
.intercept( MethodDelegation.to( ProxyConfiguration.InterceptorDispatcher.class ) )
|
||||||
|
.method( ElementMatchers.nameStartsWith( "$$_hibernate_" ).and( ElementMatchers.isVirtual() ) )
|
||||||
|
.intercept( SuperMethodCall.INSTANCE )
|
||||||
|
.defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE )
|
||||||
|
.implement( ProxyConfiguration.class )
|
||||||
|
.intercept( FieldAccessor.ofField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME ).withAssigner( Assigner.DEFAULT, Assigner.Typing.DYNAMIC ) )
|
||||||
|
.make()
|
||||||
|
.load( persistentClass.getClassLoader() )
|
||||||
|
.getLoaded();
|
||||||
|
|
||||||
|
Class previousProxy = CACHE.putIfAbsent( key, proxy );
|
||||||
|
if ( previousProxy != null ) {
|
||||||
|
proxy = previousProxy;
|
||||||
|
}
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HibernateProxy getProxy(
|
||||||
|
Serializable id,
|
||||||
|
SharedSessionContractImplementor session) throws HibernateException {
|
||||||
|
final ByteBuddyInterceptor interceptor = new ByteBuddyInterceptor(
|
||||||
|
entityName,
|
||||||
|
persistentClass,
|
||||||
|
interfaces,
|
||||||
|
id,
|
||||||
|
getIdentifierMethod,
|
||||||
|
setIdentifierMethod,
|
||||||
|
componentIdType,
|
||||||
|
session,
|
||||||
|
overridesEquals
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final HibernateProxy proxy = (HibernateProxy) proxyClass.newInstance();
|
||||||
|
( (ProxyConfiguration) proxy ).$$_hibernate_set_interceptor( interceptor );
|
||||||
|
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
LOG.error( LOG.bytecodeEnhancementFailed( entityName ), t );
|
||||||
|
throw new HibernateException( LOG.bytecodeEnhancementFailed( entityName ), t );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HibernateProxy deserializeProxy(SerializableProxy serializableProxy) {
|
||||||
|
final ByteBuddyInterceptor interceptor = new ByteBuddyInterceptor(
|
||||||
|
serializableProxy.getEntityName(),
|
||||||
|
serializableProxy.getPersistentClass(),
|
||||||
|
serializableProxy.getInterfaces(),
|
||||||
|
serializableProxy.getId(),
|
||||||
|
resolveIdGetterMethod( serializableProxy ),
|
||||||
|
resolveIdSetterMethod( serializableProxy ),
|
||||||
|
serializableProxy.getComponentIdType(),
|
||||||
|
null,
|
||||||
|
ReflectHelper.overridesEquals( serializableProxy.getPersistentClass() )
|
||||||
|
);
|
||||||
|
|
||||||
|
// note: interface is assumed to already contain HibernateProxy.class
|
||||||
|
try {
|
||||||
|
final Class proxyClass = buildProxy(
|
||||||
|
serializableProxy.getPersistentClass(),
|
||||||
|
serializableProxy.getInterfaces()
|
||||||
|
);
|
||||||
|
final HibernateProxy proxy = (HibernateProxy) proxyClass.newInstance();
|
||||||
|
( (ProxyConfiguration) proxy ).$$_hibernate_set_interceptor( interceptor );
|
||||||
|
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()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* 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.bytebuddy;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.hibernate.proxy.AbstractSerializableProxy;
|
||||||
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
|
import org.hibernate.type.CompositeType;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public SerializableProxy(
|
||||||
|
String entityName,
|
||||||
|
Class persistentClass,
|
||||||
|
Class[] interfaces,
|
||||||
|
Serializable id,
|
||||||
|
Boolean readOnly,
|
||||||
|
Method getIdentifierMethod,
|
||||||
|
Method setIdentifierMethod,
|
||||||
|
CompositeType componentIdType) {
|
||||||
|
super( entityName, id, readOnly );
|
||||||
|
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 Serializable 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readResolve() {
|
||||||
|
HibernateProxy proxy = ByteBuddyProxyFactory.deserializeProxy( this );
|
||||||
|
setReadOnlyBeforeAttachedToSession( (ByteBuddyInterceptor) proxy.getHibernateLazyInitializer() );
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
}
|
|
@ -126,8 +126,8 @@ public class JavassistProxyFactory implements ProxyFactory, Serializable {
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
catch (Throwable t) {
|
catch (Throwable t) {
|
||||||
LOG.error( LOG.javassistEnhancementFailed( entityName ), t );
|
LOG.error( LOG.bytecodeEnhancementFailed( entityName ), t );
|
||||||
throw new HibernateException( LOG.javassistEnhancementFailed( entityName ), t );
|
throw new HibernateException( LOG.bytecodeEnhancementFailed( entityName ), t );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ public class JavassistProxyFactory implements ProxyFactory, Serializable {
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
catch ( Throwable t ) {
|
catch ( Throwable t ) {
|
||||||
final String message = LOG.javassistEnhancementFailed( serializableProxy.getEntityName() );
|
final String message = LOG.bytecodeEnhancementFailed( serializableProxy.getEntityName() );
|
||||||
LOG.error( message, t );
|
LOG.error( message, t );
|
||||||
throw new HibernateException( message, t );
|
throw new HibernateException( message, t );
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,9 @@ import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javassist.ClassPool;
|
|
||||||
import javassist.CtClass;
|
|
||||||
|
|
||||||
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||||
|
import org.hibernate.cfg.Environment;
|
||||||
|
|
||||||
import org.apache.tools.ant.BuildException;
|
import org.apache.tools.ant.BuildException;
|
||||||
import org.apache.tools.ant.DirectoryScanner;
|
import org.apache.tools.ant.DirectoryScanner;
|
||||||
|
@ -39,9 +37,7 @@ import org.apache.tools.ant.types.FileSet;
|
||||||
public class EnhancementTask extends Task {
|
public class EnhancementTask extends Task {
|
||||||
private List<FileSet> filesets = new ArrayList<FileSet>();
|
private List<FileSet> filesets = new ArrayList<FileSet>();
|
||||||
|
|
||||||
// Enhancer also builds CtClass instances. Might make sense to share these (ClassPool).
|
private final Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( new DefaultEnhancementContext() );
|
||||||
private final ClassPool classPool = new ClassPool( false );
|
|
||||||
private final Enhancer enhancer = new Enhancer( new DefaultEnhancementContext() );
|
|
||||||
|
|
||||||
public void addFileset(FileSet set) {
|
public void addFileset(FileSet set) {
|
||||||
this.filesets.add( set );
|
this.filesets.add( set );
|
||||||
|
@ -72,8 +68,7 @@ public class EnhancementTask extends Task {
|
||||||
|
|
||||||
private void processClassFile(File javaClassFile) {
|
private void processClassFile(File javaClassFile) {
|
||||||
try {
|
try {
|
||||||
final CtClass ctClass = classPool.makeClass( new FileInputStream( javaClassFile ) );
|
byte[] result = enhancer.enhance( javaClassFile );
|
||||||
byte[] result = enhancer.enhance( ctClass.getName(), ctClass.toBytecode() );
|
|
||||||
if ( result != null ) {
|
if ( result != null ) {
|
||||||
writeEnhancedClass( javaClassFile, result );
|
writeEnhancedClass( javaClassFile, result );
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,9 @@ import java.io.InputStream;
|
||||||
import java.lang.instrument.IllegalClassFormatException;
|
import java.lang.instrument.IllegalClassFormatException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javassist.CtClass;
|
|
||||||
|
|
||||||
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
|
||||||
import org.hibernate.jpa.internal.enhance.EnhancingClassTransformerImpl;
|
import org.hibernate.jpa.internal.enhance.EnhancingClassTransformerImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,29 +30,36 @@ public class InstrumentedClassLoader extends ClassLoader {
|
||||||
@Override
|
@Override
|
||||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||||
// Do not instrument the following packages
|
// Do not instrument the following packages
|
||||||
if (name != null
|
if ( name != null
|
||||||
&& (name.startsWith("java.lang.") ||
|
&& ( name.startsWith( "java.lang." ) ||
|
||||||
name.startsWith("java.util.")))
|
name.startsWith( "java.util." ) ) ) {
|
||||||
return getParent().loadClass(name);
|
return getParent().loadClass( name );
|
||||||
|
}
|
||||||
Class c = findLoadedClass( name );
|
Class c = findLoadedClass( name );
|
||||||
if ( c != null ) return c;
|
if ( c != null ) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] transformed = loadClassBytes( name );
|
||||||
|
|
||||||
byte[] transformed = loadClassBytes(name);
|
|
||||||
|
|
||||||
return defineClass( name, transformed, 0, transformed.length );
|
return defineClass( name, transformed, 0, transformed.length );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized {@link ClassLoader#loadClass(String)} that returns the class
|
* Specialized {@link ClassLoader#loadClass(String)} that returns the class
|
||||||
* as a byte array.
|
* as a byte array.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
|
*
|
||||||
* @throws ClassNotFoundException
|
* @throws ClassNotFoundException
|
||||||
*/
|
*/
|
||||||
public byte[] loadClassBytes(String name) throws ClassNotFoundException {
|
public byte[] loadClassBytes(String name) throws ClassNotFoundException {
|
||||||
InputStream is = this.getResourceAsStream( name.replace( ".", "/" ) + ".class" );
|
InputStream is = this.getResourceAsStream( name.replace( ".", "/" ) + ".class" );
|
||||||
if ( is == null ) throw new ClassNotFoundException( name );
|
if ( is == null ) {
|
||||||
|
throw new ClassNotFoundException( name );
|
||||||
|
}
|
||||||
byte[] buffer = new byte[409600];
|
byte[] buffer = new byte[409600];
|
||||||
byte[] originalClass = new byte[0];
|
byte[] originalClass = new byte[0];
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
@ -64,13 +70,13 @@ public class InstrumentedClassLoader extends ClassLoader {
|
||||||
throw new ClassNotFoundException( name + " not found", e );
|
throw new ClassNotFoundException( name + " not found", e );
|
||||||
}
|
}
|
||||||
while ( r >= buffer.length ) {
|
while ( r >= buffer.length ) {
|
||||||
byte[] temp = new byte[ originalClass.length + buffer.length ];
|
byte[] temp = new byte[originalClass.length + buffer.length];
|
||||||
System.arraycopy( originalClass, 0, temp, 0, originalClass.length );
|
System.arraycopy( originalClass, 0, temp, 0, originalClass.length );
|
||||||
System.arraycopy( buffer, 0, temp, originalClass.length, buffer.length );
|
System.arraycopy( buffer, 0, temp, originalClass.length, buffer.length );
|
||||||
originalClass = temp;
|
originalClass = temp;
|
||||||
}
|
}
|
||||||
if ( r != -1 ) {
|
if ( r != -1 ) {
|
||||||
byte[] temp = new byte[ originalClass.length + r ];
|
byte[] temp = new byte[originalClass.length + r];
|
||||||
System.arraycopy( originalClass, 0, temp, 0, originalClass.length );
|
System.arraycopy( originalClass, 0, temp, 0, originalClass.length );
|
||||||
System.arraycopy( buffer, 0, temp, originalClass.length, r );
|
System.arraycopy( buffer, 0, temp, originalClass.length, r );
|
||||||
originalClass = temp;
|
originalClass = temp;
|
||||||
|
@ -84,13 +90,20 @@ public class InstrumentedClassLoader extends ClassLoader {
|
||||||
|
|
||||||
EnhancingClassTransformerImpl t = new EnhancingClassTransformerImpl( getEnhancementContext( getParent(), entities ) );
|
EnhancingClassTransformerImpl t = new EnhancingClassTransformerImpl( getEnhancementContext( getParent(), entities ) );
|
||||||
try {
|
try {
|
||||||
return t.transform(
|
byte[] transformed = t.transform(
|
||||||
getParent(),
|
getParent(),
|
||||||
name,
|
name,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
originalClass
|
originalClass
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( transformed == null ) {
|
||||||
|
return originalClass;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return transformed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (IllegalClassFormatException e) {
|
catch (IllegalClassFormatException e) {
|
||||||
throw new ClassNotFoundException( name + " not found", e );
|
throw new ClassNotFoundException( name + " not found", e );
|
||||||
|
@ -110,12 +123,12 @@ public class InstrumentedClassLoader extends ClassLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEntityClass(CtClass classDescriptor) {
|
public boolean isEntityClass(UnloadedClass classDescriptor) {
|
||||||
return entities.contains( classDescriptor.getName() ) && super.isEntityClass( classDescriptor );
|
return entities.contains( classDescriptor.getName() ) && super.isEntityClass( classDescriptor );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompositeClass(CtClass classDescriptor) {
|
public boolean isCompositeClass(UnloadedClass classDescriptor) {
|
||||||
return entities.contains( classDescriptor.getName() ) && super.isCompositeClass( classDescriptor );
|
return entities.contains( classDescriptor.getName() ) && super.isCompositeClass( classDescriptor );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,24 +6,14 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.jpa.test.enhancement;
|
package org.hibernate.jpa.test.enhancement;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javassist.ClassPool;
|
|
||||||
import javassist.CtClass;
|
|
||||||
import javassist.CtMethod;
|
|
||||||
import javassist.bytecode.AttributeInfo;
|
|
||||||
import javassist.bytecode.StackMapTable;
|
|
||||||
|
|
||||||
import org.hibernate.engine.spi.Managed;
|
import org.hibernate.engine.spi.Managed;
|
||||||
import org.hibernate.engine.spi.ManagedComposite;
|
import org.hibernate.engine.spi.ManagedComposite;
|
||||||
import org.hibernate.engine.spi.ManagedEntity;
|
import org.hibernate.engine.spi.ManagedEntity;
|
||||||
import org.hibernate.jpa.test.enhancement.cases.domain.Simple;
|
import org.hibernate.jpa.test.enhancement.cases.domain.Simple;
|
||||||
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -36,10 +26,10 @@ import static org.junit.Assert.assertTrue;
|
||||||
* @author Dustin Schultz
|
* @author Dustin Schultz
|
||||||
*/
|
*/
|
||||||
public class InterceptFieldClassFileTransformerTest {
|
public class InterceptFieldClassFileTransformerTest {
|
||||||
|
|
||||||
private List<String> entities = new ArrayList<String>();
|
private List<String> entities = new ArrayList<String>();
|
||||||
private InstrumentedClassLoader loader = null;
|
private InstrumentedClassLoader loader = null;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
entities.add( Simple.class.getName() );
|
entities.add( Simple.class.getName() );
|
||||||
|
@ -48,13 +38,13 @@ public class InterceptFieldClassFileTransformerTest {
|
||||||
cl.setEntities( entities );
|
cl.setEntities( entities );
|
||||||
this.loader = cl;
|
this.loader = cl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that class file enhancement works.
|
* Tests that class file enhancement works.
|
||||||
*
|
*
|
||||||
* @throws Exception in case the test fails.
|
* @throws Exception in case the test fails.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testEnhancement() throws Exception {
|
public void testEnhancement() throws Exception {
|
||||||
// sanity check that the class is unmodified and does not contain getFieldHandler()
|
// sanity check that the class is unmodified and does not contain getFieldHandler()
|
||||||
assertFalse( implementsManaged( Simple.class ) );
|
assertFalse( implementsManaged( Simple.class ) );
|
||||||
|
@ -75,35 +65,4 @@ public class InterceptFieldClassFileTransformerTest {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that methods that were enhanced by javassist have
|
|
||||||
* StackMapTables for java verification. Without these,
|
|
||||||
* java.lang.VerifyError's occur in JDK7.
|
|
||||||
*
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
* @throws InstantiationException
|
|
||||||
* @throws IllegalAccessException
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
@TestForIssue(jiraKey = "HHH-7747")
|
|
||||||
public void testStackMapTableEnhancment() throws ClassNotFoundException,
|
|
||||||
InstantiationException, IllegalAccessException, IOException {
|
|
||||||
byte[] classBytes = loader.loadClassBytes(entities.get(0));
|
|
||||||
ClassPool classPool = new ClassPool();
|
|
||||||
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(
|
|
||||||
classBytes));
|
|
||||||
for (CtMethod ctMethod : ctClass.getMethods()) {
|
|
||||||
//Only check methods that were added by javassist
|
|
||||||
if (ctMethod.getName().startsWith("$javassist_")) {
|
|
||||||
AttributeInfo attributeInfo = ctMethod
|
|
||||||
.getMethodInfo().getCodeAttribute()
|
|
||||||
.getAttribute(StackMapTable.tag);
|
|
||||||
Assert.assertNotNull(attributeInfo);
|
|
||||||
StackMapTable smt = (StackMapTable)attributeInfo;
|
|
||||||
Assert.assertNotNull(smt.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,18 +75,18 @@ public class DuplicatedDiscriminatorValueTest extends BaseUnitTestCase {
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@DiscriminatorValue(DISCRIMINATOR_VALUE) // Duplicated discriminator value in single hierarchy.
|
@DiscriminatorValue(DISCRIMINATOR_VALUE) // Duplicated discriminator value in single hierarchy.
|
||||||
private static class Building1 extends Building {
|
public static class Building1 extends Building {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@DiscriminatorValue(DISCRIMINATOR_VALUE) // Duplicated discriminator value in single hierarchy.
|
@DiscriminatorValue(DISCRIMINATOR_VALUE) // Duplicated discriminator value in single hierarchy.
|
||||||
private static class Building2 extends Building {
|
public static class Building2 extends Building {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@DiscriminatorColumn(name = "entity_type")
|
@DiscriminatorColumn(name = "entity_type")
|
||||||
@DiscriminatorValue("F")
|
@DiscriminatorValue("F")
|
||||||
private static class Furniture {
|
public static class Furniture {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
private Integer id;
|
private Integer id;
|
||||||
|
@ -94,6 +94,6 @@ public class DuplicatedDiscriminatorValueTest extends BaseUnitTestCase {
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@DiscriminatorValue(DISCRIMINATOR_VALUE) // Duplicated discriminator value in different hierarchy.
|
@DiscriminatorValue(DISCRIMINATOR_VALUE) // Duplicated discriminator value in different hierarchy.
|
||||||
private static class Chair extends Furniture {
|
public static class Chair extends Furniture {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,40 +4,20 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* 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>.
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.bytecode.javassist;
|
package org.hibernate.test.bytecode;
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl;
|
|
||||||
import org.hibernate.cfg.Environment;
|
|
||||||
|
|
||||||
import org.hibernate.testing.Skip;
|
|
||||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
import org.hibernate.test.bytecode.Bean;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that the Javassist-based lazy initializer properly handles InvocationTargetExceptions
|
|
||||||
*
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
@Skip(
|
|
||||||
condition = InvocationTargetExceptionTest.LocalSkipMatcher.class,
|
|
||||||
message = "environment not configured for javassist bytecode provider"
|
|
||||||
)
|
|
||||||
public class InvocationTargetExceptionTest extends BaseCoreFunctionalTestCase {
|
public class InvocationTargetExceptionTest extends BaseCoreFunctionalTestCase {
|
||||||
public static class LocalSkipMatcher implements Skip.Matcher {
|
|
||||||
@Override
|
|
||||||
public boolean isMatch() {
|
|
||||||
return ! BytecodeProviderImpl.class.isInstance( Environment.getBytecodeProvider() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getMappings() {
|
public String[] getMappings() {
|
||||||
return new String[] { "bytecode/Bean.hbm.xml" };
|
return new String[] { "bytecode/Bean.hbm.xml" };
|
|
@ -4,15 +4,15 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* 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>.
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.bytecode.javassist;
|
package org.hibernate.test.bytecode;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.hibernate.bytecode.internal.javassist.BulkAccessor;
|
import org.hibernate.bytecode.internal.javassist.BulkAccessor;
|
||||||
import org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl;
|
import org.hibernate.bytecode.spi.BytecodeProvider;
|
||||||
import org.hibernate.bytecode.spi.ReflectionOptimizer;
|
import org.hibernate.bytecode.spi.ReflectionOptimizer;
|
||||||
import org.hibernate.test.bytecode.Bean;
|
import org.hibernate.cfg.Environment;
|
||||||
import org.hibernate.test.bytecode.BeanReflectionHelper;
|
|
||||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -34,7 +34,7 @@ public class ReflectionOptimizerTest extends BaseUnitTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReflectionOptimization() {
|
public void testReflectionOptimization() {
|
||||||
BytecodeProviderImpl provider = new BytecodeProviderImpl();
|
BytecodeProvider provider = Environment.getBytecodeProvider();
|
||||||
ReflectionOptimizer optimizer = provider.getReflectionOptimizer(
|
ReflectionOptimizer optimizer = provider.getReflectionOptimizer(
|
||||||
Bean.class,
|
Bean.class,
|
||||||
BeanReflectionHelper.getGetterNames(),
|
BeanReflectionHelper.getGetterNames(),
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.bytecode.enhancement;
|
package org.hibernate.test.bytecode.enhancement;
|
||||||
|
|
||||||
import javassist.CtClass;
|
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
|
||||||
|
|
||||||
import org.hibernate.test.bytecode.enhancement.association.InheritedAttributeAssociationTestTask;
|
import org.hibernate.test.bytecode.enhancement.association.InheritedAttributeAssociationTestTask;
|
||||||
import org.hibernate.test.bytecode.enhancement.lazy.group.LazyGroupUpdateTestTask;
|
import org.hibernate.test.bytecode.enhancement.lazy.group.LazyGroupUpdateTestTask;
|
||||||
|
@ -50,6 +50,7 @@ import org.hibernate.test.bytecode.enhancement.merge.CompositeMergeTestTask;
|
||||||
import org.hibernate.test.bytecode.enhancement.ondemandload.LazyCollectionWithClearedSessionTestTask;
|
import org.hibernate.test.bytecode.enhancement.ondemandload.LazyCollectionWithClearedSessionTestTask;
|
||||||
import org.hibernate.test.bytecode.enhancement.ondemandload.LazyCollectionWithClosedSessionTestTask;
|
import org.hibernate.test.bytecode.enhancement.ondemandload.LazyCollectionWithClosedSessionTestTask;
|
||||||
import org.hibernate.test.bytecode.enhancement.ondemandload.LazyEntityLoadingWithClosedSessionTestTask;
|
import org.hibernate.test.bytecode.enhancement.ondemandload.LazyEntityLoadingWithClosedSessionTestTask;
|
||||||
|
import org.hibernate.test.bytecode.enhancement.otherentityentrycontext.OtherEntityEntryContextTestTask;
|
||||||
import org.hibernate.test.bytecode.enhancement.pk.EmbeddedPKTestTask;
|
import org.hibernate.test.bytecode.enhancement.pk.EmbeddedPKTestTask;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -112,7 +113,7 @@ public class EnhancerTest extends BaseUnitTestCase {
|
||||||
public void testLazyProxyOnEnhancedEntity() {
|
public void testLazyProxyOnEnhancedEntity() {
|
||||||
EnhancerTestUtils.runEnhancerTestTask( LazyProxyOnEnhancedEntityTestTask.class, new EnhancerTestContext() {
|
EnhancerTestUtils.runEnhancerTestTask( LazyProxyOnEnhancedEntityTestTask.class, new EnhancerTestContext() {
|
||||||
@Override
|
@Override
|
||||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
@ -176,7 +177,7 @@ public class EnhancerTest extends BaseUnitTestCase {
|
||||||
EnhancerTestUtils.runEnhancerTestTask( MappedSuperclassTestTask.class );
|
EnhancerTestUtils.runEnhancerTestTask( MappedSuperclassTestTask.class );
|
||||||
EnhancerTestUtils.runEnhancerTestTask( MappedSuperclassTestTask.class, new EnhancerTestContext() {
|
EnhancerTestUtils.runEnhancerTestTask( MappedSuperclassTestTask.class, new EnhancerTestContext() {
|
||||||
@Override
|
@Override
|
||||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
|
||||||
// HHH-10981 - Without lazy loading, the generation of getters and setters has a different code path
|
// HHH-10981 - Without lazy loading, the generation of getters and setters has a different code path
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class ProxyInterfaceClassLoaderTest extends BaseCoreFunctionalTestCase {
|
||||||
s.close();
|
s.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPerson {
|
public interface IPerson {
|
||||||
|
|
||||||
int getId();
|
int getId();
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ dependencies {
|
||||||
compile( libraries.commons_annotations )
|
compile( libraries.commons_annotations )
|
||||||
compile( libraries.jpa )
|
compile( libraries.jpa )
|
||||||
compile( libraries.javassist )
|
compile( libraries.javassist )
|
||||||
|
compile( libraries.byteBuddy )
|
||||||
compile( libraries.jta )
|
compile( libraries.jta )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ dependencies {
|
||||||
testCompile( project( ':hibernate-testing' ) )
|
testCompile( project( ':hibernate-testing' ) )
|
||||||
testCompile( project( path: ':hibernate-core', configuration: 'tests' ) )
|
testCompile( project( path: ':hibernate-core', configuration: 'tests' ) )
|
||||||
testRuntime( libraries.javassist )
|
testRuntime( libraries.javassist )
|
||||||
|
testRuntime( libraries.byteBuddy )
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Java 9 ftw!
|
// Java 9 ftw!
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.envers.internal.tools;
|
package org.hibernate.envers.internal.tools;
|
||||||
|
|
||||||
import javassist.util.proxy.ProxyFactory;
|
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
|
@ -76,7 +75,7 @@ public abstract class EntityTools {
|
||||||
if ( clazz == null ) {
|
if ( clazz == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else if ( ProxyFactory.isProxyClass( clazz ) ) {
|
else if ( HibernateProxy.class.isAssignableFrom( clazz ) ) {
|
||||||
// Get the source class of Javassist proxy instance.
|
// Get the source class of Javassist proxy instance.
|
||||||
return (Class<T>) clazz.getSuperclass();
|
return (Class<T>) clazz.getSuperclass();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ ext {
|
||||||
minorSlot: minorSlot,
|
minorSlot: minorSlot,
|
||||||
version: rootProject.hibernateTargetVersion,
|
version: rootProject.hibernateTargetVersion,
|
||||||
wildflyVersion: wildflyVersion,
|
wildflyVersion: wildflyVersion,
|
||||||
javassistVersion: javassistVersion
|
javassistVersion: javassistVersion,
|
||||||
|
byteBuddyVersion: byteBuddyVersion
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,12 +55,14 @@ configurations {
|
||||||
wildflyDist
|
wildflyDist
|
||||||
|
|
||||||
javassist
|
javassist
|
||||||
|
byteBuddy
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
jipijapa "org.wildfly:jipijapa-hibernate5:${wildflyVersion}"
|
jipijapa "org.wildfly:jipijapa-hibernate5:${wildflyVersion}"
|
||||||
wildflyDist "org.wildfly:wildfly-dist:${wildflyVersion}@zip"
|
wildflyDist "org.wildfly:wildfly-dist:${wildflyVersion}@zip"
|
||||||
javassist libraries.javassist
|
javassist libraries.javassist
|
||||||
|
byteBuddy libraries.byteBuddy
|
||||||
|
|
||||||
testCompile project( ":hibernate-core" )
|
testCompile project( ":hibernate-core" )
|
||||||
testCompile project( ":hibernate-envers" )
|
testCompile project( ":hibernate-envers" )
|
||||||
|
@ -121,6 +124,8 @@ task createModulesZip(type: Zip, dependsOn: [copyAndExpandModuleXml]) {
|
||||||
|
|
||||||
// also need Javassist's jar
|
// also need Javassist's jar
|
||||||
from configurations.javassist
|
from configurations.javassist
|
||||||
|
// also need Byte Buddy's jar
|
||||||
|
from configurations.byteBuddy
|
||||||
}
|
}
|
||||||
|
|
||||||
into( 'org/hibernate/infinispan/' + slot ) {
|
into( 'org/hibernate/infinispan/' + slot ) {
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
<resource-root path="hibernate-core-${version}.jar"/>
|
<resource-root path="hibernate-core-${version}.jar"/>
|
||||||
<resource-root path="hibernate-envers-${version}.jar"/>
|
<resource-root path="hibernate-envers-${version}.jar"/>
|
||||||
<resource-root path="javassist-${javassistVersion}.jar"/>
|
<resource-root path="javassist-${javassistVersion}.jar"/>
|
||||||
|
<resource-root path="byte-buddy-${byteBuddyVersion}.jar"/>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -47,6 +47,7 @@ dependencies {
|
||||||
|
|
||||||
testRuntime('jaxen:jaxen:1.1')
|
testRuntime('jaxen:jaxen:1.1')
|
||||||
testRuntime(libraries.javassist)
|
testRuntime(libraries.javassist)
|
||||||
|
testRuntime(libraries.byteBuddy)
|
||||||
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -10,6 +10,8 @@ import javassist.CtClass;
|
||||||
import javassist.CtField;
|
import javassist.CtField;
|
||||||
|
|
||||||
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enhancement context used in tests
|
* Enhancement context used in tests
|
||||||
|
@ -19,27 +21,27 @@ import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
||||||
public class EnhancerTestContext extends DefaultEnhancementContext {
|
public class EnhancerTestContext extends DefaultEnhancementContext {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doBiDirectionalAssociationManagement(CtField field) {
|
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doExtendedEnhancement(CtClass classDescriptor) {
|
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLazyLoadable(CtField field) {
|
public boolean isLazyLoadable(UnloadedField field) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.hibernate.HibernateException;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||||
|
import org.hibernate.cfg.Environment;
|
||||||
import org.hibernate.engine.internal.MutableEntityEntryFactory;
|
import org.hibernate.engine.internal.MutableEntityEntryFactory;
|
||||||
import org.hibernate.engine.spi.EntityEntry;
|
import org.hibernate.engine.spi.EntityEntry;
|
||||||
import org.hibernate.engine.spi.SelfDirtinessTracker;
|
import org.hibernate.engine.spi.SelfDirtinessTracker;
|
||||||
|
@ -57,8 +58,8 @@ public abstract class EnhancerTestUtils extends BaseUnitTestCase {
|
||||||
CtClass entityCtClass = generateCtClassForAnEntity( classToEnhance );
|
CtClass entityCtClass = generateCtClassForAnEntity( classToEnhance );
|
||||||
|
|
||||||
byte[] original = entityCtClass.toBytecode();
|
byte[] original = entityCtClass.toBytecode();
|
||||||
byte[] enhanced = new Enhancer( new EnhancerTestContext() ).enhance( entityCtClass.getName(), original );
|
byte[] enhanced = Environment.getBytecodeProvider().getEnhancer( new EnhancerTestContext() ).enhance( entityCtClass.getName(), original );
|
||||||
assertFalse( "entity was not enhanced", Arrays.equals( original, enhanced ) );
|
assertFalse( "entity was not enhanced", enhanced == null );
|
||||||
log.infof( "enhanced entity [%s]", entityCtClass.getName() );
|
log.infof( "enhanced entity [%s]", entityCtClass.getName() );
|
||||||
|
|
||||||
ClassPool cp = new ClassPool( false );
|
ClassPool cp = new ClassPool( false );
|
||||||
|
@ -127,7 +128,7 @@ public abstract class EnhancerTestUtils extends BaseUnitTestCase {
|
||||||
private static ClassLoader getEnhancerClassLoader(EnhancementContext context, String packageName) {
|
private static ClassLoader getEnhancerClassLoader(EnhancementContext context, String packageName) {
|
||||||
return new ClassLoader() {
|
return new ClassLoader() {
|
||||||
|
|
||||||
private Enhancer enhancer = new Enhancer( context );
|
private Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( context );
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
@Override
|
@Override
|
||||||
|
@ -151,12 +152,17 @@ public abstract class EnhancerTestUtils extends BaseUnitTestCase {
|
||||||
|
|
||||||
byte[] enhanced = enhancer.enhance( name, original );
|
byte[] enhanced = enhancer.enhance( name, original );
|
||||||
|
|
||||||
File f = new File( workingDir + File.separator + name.replace( ".", File.separator ) + ".class" );
|
if ( enhanced != null ) {
|
||||||
f.getParentFile().mkdirs();
|
File f = new File( workingDir + File.separator + name.replace( ".", File.separator ) + ".class" );
|
||||||
f.createNewFile();
|
f.getParentFile().mkdirs();
|
||||||
FileOutputStream out = new FileOutputStream( f );
|
f.createNewFile();
|
||||||
out.write( enhanced );
|
FileOutputStream out = new FileOutputStream( f );
|
||||||
out.close();
|
out.write( enhanced );
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
enhanced = original;
|
||||||
|
}
|
||||||
|
|
||||||
return defineClass( name, enhanced, 0, enhanced.length );
|
return defineClass( name, enhanced, 0, enhanced.length );
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ ext {
|
||||||
cdiVersion = '1.1'
|
cdiVersion = '1.1'
|
||||||
|
|
||||||
javassistVersion = '3.20.0-GA'
|
javassistVersion = '3.20.0-GA'
|
||||||
|
byteBuddyVersion = '1.5.4'
|
||||||
|
|
||||||
// Wildfly version targeted by module ZIP; Arquillian/Shrinkwrap versions used for CDI testing and testing the module ZIP
|
// Wildfly version targeted by module ZIP; Arquillian/Shrinkwrap versions used for CDI testing and testing the module ZIP
|
||||||
wildflyVersion = '10.0.0.Final'
|
wildflyVersion = '10.0.0.Final'
|
||||||
|
@ -48,6 +49,9 @@ ext {
|
||||||
// Javassist
|
// Javassist
|
||||||
javassist: "org.javassist:javassist:${javassistVersion}",
|
javassist: "org.javassist:javassist:${javassistVersion}",
|
||||||
|
|
||||||
|
// Byte Buddy
|
||||||
|
byteBuddy: "net.bytebuddy:byte-buddy:${byteBuddyVersion}",
|
||||||
|
|
||||||
// javax
|
// javax
|
||||||
jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final',
|
jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final',
|
||||||
// There is a bug in the OSGi information in the JBoss one. See https://issues.jboss.org/browse/JBEE-160
|
// There is a bug in the OSGi information in the JBoss one. See https://issues.jboss.org/browse/JBEE-160
|
||||||
|
|
|
@ -29,6 +29,7 @@ dependencies {
|
||||||
compile( project(':hibernate-core') ) { transitive = false }
|
compile( project(':hibernate-core') ) { transitive = false }
|
||||||
compile( libraries.jpa ) { transitive = false }
|
compile( libraries.jpa ) { transitive = false }
|
||||||
compile( libraries.javassist ) { transitive = false }
|
compile( libraries.javassist ) { transitive = false }
|
||||||
|
compile( libraries.byteBuddy ) { transitive = false }
|
||||||
compile 'org.codehaus.plexus:plexus-utils:3.0.1'
|
compile 'org.codehaus.plexus:plexus-utils:3.0.1'
|
||||||
runtime( libraries.maven_core )
|
runtime( libraries.maven_core )
|
||||||
runtime( libraries.maven_artifact )
|
runtime( libraries.maven_artifact )
|
||||||
|
@ -37,6 +38,7 @@ dependencies {
|
||||||
runtime( project(':hibernate-core') )
|
runtime( project(':hibernate-core') )
|
||||||
runtime( libraries.jpa )
|
runtime( libraries.jpa )
|
||||||
runtime( libraries.javassist )
|
runtime( libraries.javassist )
|
||||||
|
runtime( libraries.byteBuddy )
|
||||||
runtime 'org.codehaus.plexus:plexus-utils:3.0.1'
|
runtime 'org.codehaus.plexus:plexus-utils:3.0.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +58,7 @@ task processPluginXml(type: Copy) {
|
||||||
+ generateMavenDependency(libraries.jta)\
|
+ generateMavenDependency(libraries.jta)\
|
||||||
+ generateMavenDependency(libraries.commons_annotations)\
|
+ generateMavenDependency(libraries.commons_annotations)\
|
||||||
+ generateMavenDependency(libraries.javassist)\
|
+ generateMavenDependency(libraries.javassist)\
|
||||||
|
+ generateMavenDependency(libraries.byteBuddy)\
|
||||||
+ generateMavenDependency(libraries.logging)\
|
+ generateMavenDependency(libraries.logging)\
|
||||||
+ generateMavenDependency("org.hibernate:hibernate-core:" + project.version)])
|
+ generateMavenDependency("org.hibernate:hibernate-core:" + project.version)])
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,9 @@ package org.hibernate.orm.tooling.maven;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
@ -21,10 +19,6 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javassist.ClassPool;
|
|
||||||
import javassist.CtClass;
|
|
||||||
import javassist.CtField;
|
|
||||||
|
|
||||||
import org.apache.maven.artifact.Artifact;
|
import org.apache.maven.artifact.Artifact;
|
||||||
import org.apache.maven.plugin.AbstractMojo;
|
import org.apache.maven.plugin.AbstractMojo;
|
||||||
import org.apache.maven.plugin.MojoExecutionException;
|
import org.apache.maven.plugin.MojoExecutionException;
|
||||||
|
@ -39,6 +33,9 @@ import org.apache.maven.project.MavenProject;
|
||||||
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedField;
|
||||||
|
import org.hibernate.cfg.Environment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This plugin will enhance Entity objects.
|
* This plugin will enhance Entity objects.
|
||||||
|
@ -105,27 +102,27 @@ public class MavenEnhancePlugin extends AbstractMojo {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doBiDirectionalAssociationManagement(CtField field) {
|
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
|
||||||
return enableAssociationManagement;
|
return enableAssociationManagement;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
|
||||||
return enableDirtyTracking;
|
return enableDirtyTracking;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
|
||||||
return enableLazyInitialization;
|
return enableLazyInitialization;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLazyLoadable(CtField field) {
|
public boolean isLazyLoadable(UnloadedField field) {
|
||||||
return enableLazyInitialization;
|
return enableLazyInitialization;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doExtendedEnhancement(CtClass classDescriptor) {
|
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
|
||||||
return enableExtendedEnhancement;
|
return enableExtendedEnhancement;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -134,28 +131,19 @@ public class MavenEnhancePlugin extends AbstractMojo {
|
||||||
getLog().warn( "Extended enhancement is enabled. Classes other than entities may be modified. You should consider access the entities using getter/setter methods and disable this property. Use at your own risk." );
|
getLog().warn( "Extended enhancement is enabled. Classes other than entities may be modified. You should consider access the entities using getter/setter methods and disable this property. Use at your own risk." );
|
||||||
}
|
}
|
||||||
|
|
||||||
final Enhancer enhancer = new Enhancer( enhancementContext );
|
final Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( enhancementContext );
|
||||||
final ClassPool classPool = new ClassPool( false );
|
|
||||||
|
|
||||||
for ( File file : sourceSet ) {
|
for ( File file : sourceSet ) {
|
||||||
final CtClass ctClass = toCtClass( file, classPool );
|
|
||||||
if ( ctClass == null ) {
|
final byte[] enhancedBytecode = doEnhancement( file, enhancer );
|
||||||
|
|
||||||
|
if ( enhancedBytecode == null ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !enableLazyInitialization ) {
|
writeOutEnhancedClass( enhancedBytecode, file );
|
||||||
if ( !enhancementContext.isEntityClass( ctClass )
|
|
||||||
&& !enhancementContext.isCompositeClass( ctClass )
|
|
||||||
&& !enhancementContext.isMappedSuperclassClass( ctClass ) ) {
|
|
||||||
getLog().info( "Skipping class file [" + file.getAbsolutePath() + "], not an entity nor embeddable" );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final byte[] enhancedBytecode = doEnhancement( ctClass, enhancer );
|
getLog().info( "Successfully enhanced class [" + file + "]" );
|
||||||
writeOutEnhancedClass( enhancedBytecode, ctClass, file );
|
|
||||||
|
|
||||||
getLog().info( "Successfully enhanced class [" + ctClass.getName() + "]" );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,47 +192,12 @@ public class MavenEnhancePlugin extends AbstractMojo {
|
||||||
return new URLClassLoader( urls.toArray( new URL[urls.size()] ), Enhancer.class.getClassLoader() );
|
return new URLClassLoader( urls.toArray( new URL[urls.size()] ), Enhancer.class.getClassLoader() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private CtClass toCtClass(File file, ClassPool classPool) throws MojoExecutionException {
|
private byte[] doEnhancement(File javaClassFile, Enhancer enhancer) throws MojoExecutionException {
|
||||||
try {
|
try {
|
||||||
final InputStream is = new FileInputStream( file.getAbsolutePath() );
|
return enhancer.enhance(javaClassFile);
|
||||||
|
|
||||||
try {
|
|
||||||
return classPool.makeClass( is );
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
String msg = "Javassist unable to load class in preparation for enhancing: " + file.getAbsolutePath();
|
|
||||||
if ( failOnError ) {
|
|
||||||
throw new MojoExecutionException( msg, e );
|
|
||||||
}
|
|
||||||
getLog().warn( msg );
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
try {
|
|
||||||
is.close();
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
getLog().info( "Was unable to close InputStream : " + file.getAbsolutePath(), e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException e) {
|
|
||||||
// should never happen, but...
|
|
||||||
String msg = "Unable to locate class file for InputStream: " + file.getAbsolutePath();
|
|
||||||
if ( failOnError ) {
|
|
||||||
throw new MojoExecutionException( msg, e );
|
|
||||||
}
|
|
||||||
getLog().warn( msg );
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] doEnhancement(CtClass ctClass, Enhancer enhancer) throws MojoExecutionException {
|
|
||||||
try {
|
|
||||||
return enhancer.enhance( ctClass.getName(), ctClass.toBytecode() );
|
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
String msg = "Unable to enhance class: " + ctClass.getName();
|
String msg = "Unable to enhance class: " + javaClassFile.getName();
|
||||||
if ( failOnError ) {
|
if ( failOnError ) {
|
||||||
throw new MojoExecutionException( msg, e );
|
throw new MojoExecutionException( msg, e );
|
||||||
}
|
}
|
||||||
|
@ -283,22 +236,19 @@ public class MavenEnhancePlugin extends AbstractMojo {
|
||||||
Collections.addAll( this.sourceSet, files );
|
Collections.addAll( this.sourceSet, files );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeOutEnhancedClass(byte[] enhancedBytecode, CtClass ctClass, File file) throws MojoExecutionException{
|
private void writeOutEnhancedClass(byte[] enhancedBytecode, File file) throws MojoExecutionException {
|
||||||
if ( enhancedBytecode == null ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
if ( file.delete() ) {
|
if ( file.delete() ) {
|
||||||
if ( !file.createNewFile() ) {
|
if ( !file.createNewFile() ) {
|
||||||
getLog().error( "Unable to recreate class file [" + ctClass.getName() + "]" );
|
getLog().error( "Unable to recreate class file [" + file.getName() + "]" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
getLog().error( "Unable to delete class file [" + ctClass.getName() + "]" );
|
getLog().error( "Unable to delete class file [" + file.getName() + "]" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
getLog().warn( "Problem preparing class file for writing out enhancements [" + ctClass.getName() + "]" );
|
getLog().warn( "Problem preparing class file for writing out enhancements [" + file.getName() + "]" );
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -308,7 +258,7 @@ public class MavenEnhancePlugin extends AbstractMojo {
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
String msg = String.format( "Error writing to enhanced class [%s] to file [%s]", ctClass.getName(), file.getAbsolutePath() );
|
String msg = String.format( "Error writing to enhanced class [%s] to file [%s]", file.getName(), file.getAbsolutePath() );
|
||||||
if ( failOnError ) {
|
if ( failOnError ) {
|
||||||
throw new MojoExecutionException( msg, e );
|
throw new MojoExecutionException( msg, e );
|
||||||
}
|
}
|
||||||
|
@ -317,7 +267,6 @@ public class MavenEnhancePlugin extends AbstractMojo {
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
ctClass.detach();
|
|
||||||
}
|
}
|
||||||
catch (IOException ignore) {
|
catch (IOException ignore) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ dependencies {
|
||||||
compile( project( ':hibernate-core' ) )
|
compile( project( ':hibernate-core' ) )
|
||||||
compile( libraries.jpa )
|
compile( libraries.jpa )
|
||||||
compile( libraries.javassist )
|
compile( libraries.javassist )
|
||||||
|
compile( libraries.byteBuddy )
|
||||||
compile gradleApi()
|
compile gradleApi()
|
||||||
compile localGroovy()
|
compile localGroovy()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,21 +7,15 @@
|
||||||
package org.hibernate.orm.tooling.gradle;
|
package org.hibernate.orm.tooling.gradle;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javassist.ClassPool;
|
|
||||||
import javassist.CtClass;
|
|
||||||
import javassist.CtField;
|
|
||||||
|
|
||||||
import org.gradle.api.Action;
|
import org.gradle.api.Action;
|
||||||
import org.gradle.api.GradleException;
|
import org.gradle.api.GradleException;
|
||||||
import org.gradle.api.Plugin;
|
import org.gradle.api.Plugin;
|
||||||
|
@ -36,6 +30,9 @@ import org.gradle.api.tasks.SourceSet;
|
||||||
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
|
||||||
|
import org.hibernate.bytecode.enhance.spi.UnloadedField;
|
||||||
|
import org.hibernate.cfg.Environment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Hibernate Gradle plugin. Adds Hibernate build-time capabilities into your Gradle-based build.
|
* The Hibernate Gradle plugin. Adds Hibernate build-time capabilities into your Gradle-based build.
|
||||||
|
@ -92,37 +89,36 @@ public class HibernatePlugin implements Plugin<Project> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doBiDirectionalAssociationManagement(CtField field) {
|
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
|
||||||
return hibernateExtension.enhance.getEnableAssociationManagement();
|
return hibernateExtension.enhance.getEnableAssociationManagement();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
|
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
|
||||||
return hibernateExtension.enhance.getEnableDirtyTracking();
|
return hibernateExtension.enhance.getEnableDirtyTracking();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
|
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
|
||||||
return hibernateExtension.enhance.getEnableLazyInitialization();
|
return hibernateExtension.enhance.getEnableLazyInitialization();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLazyLoadable(CtField field) {
|
public boolean isLazyLoadable(UnloadedField field) {
|
||||||
return hibernateExtension.enhance.getEnableLazyInitialization();
|
return hibernateExtension.enhance.getEnableLazyInitialization();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doExtendedEnhancement(CtClass classDescriptor) {
|
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
|
||||||
return hibernateExtension.enhance.getEnableExtendedEnhancement();
|
return hibernateExtension.enhance.getEnableExtendedEnhancement();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( hibernateExtension.enhance.getEnableExtendedEnhancement() ) {
|
if ( hibernateExtension.enhance.getEnableExtendedEnhancement() ) {
|
||||||
logger.warn( "Extended enhancement is enabled. Classes other than entities may be modified. You should consider access the entities using getter/setter methods and disable this property. Use at your own risk." );
|
logger.warn("Extended enhancement is enabled. Classes other than entities may be modified. You should consider access the entities using getter/setter methods and disable this property. Use at your own risk." );
|
||||||
}
|
}
|
||||||
|
|
||||||
final Enhancer enhancer = new Enhancer( enhancementContext );
|
final Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( enhancementContext );
|
||||||
final ClassPool classPool = new ClassPool( false );
|
|
||||||
|
|
||||||
final FileTree fileTree = project.fileTree( sourceSet.getOutput().getClassesDir() );
|
final FileTree fileTree = project.fileTree( sourceSet.getOutput().getClassesDir() );
|
||||||
for ( File file : fileTree ) {
|
for ( File file : fileTree ) {
|
||||||
|
@ -130,19 +126,14 @@ public class HibernatePlugin implements Plugin<Project> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final CtClass ctClass = toCtClass( file, classPool );
|
final byte[] enhancedBytecode = doEnhancement( file, enhancer );
|
||||||
|
if ( enhancedBytecode != null ) {
|
||||||
if ( !enhancementContext.isEntityClass( ctClass )
|
writeOutEnhancedClass( enhancedBytecode, file );
|
||||||
&& !enhancementContext.isCompositeClass( ctClass )
|
logger.info( "Successfully enhanced class [" + file + "]" );
|
||||||
&& !enhancementContext.isMappedSuperclassClass( ctClass ) ) {
|
}
|
||||||
logger.info( "Skipping class [" + file.getAbsolutePath() + "], not an entity nor embeddable" );
|
else {
|
||||||
continue;
|
logger.info( "Skipping class [" + file.getAbsolutePath() + "], not an entity nor embeddable" );
|
||||||
}
|
}
|
||||||
|
|
||||||
final byte[] enhancedBytecode = doEnhancement( ctClass, enhancer );
|
|
||||||
writeOutEnhancedClass( enhancedBytecode, ctClass, file );
|
|
||||||
|
|
||||||
logger.info( "Successfully enhanced class [" + ctClass.getName() + "]" );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,53 +155,28 @@ public class HibernatePlugin implements Plugin<Project> {
|
||||||
return new URLClassLoader( urls.toArray( new URL[urls.size()] ), Enhancer.class.getClassLoader() );
|
return new URLClassLoader( urls.toArray( new URL[urls.size()] ), Enhancer.class.getClassLoader() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private CtClass toCtClass(File file, ClassPool classPool) {
|
private byte[] doEnhancement(File javaClassFile, Enhancer enhancer) {
|
||||||
try {
|
try {
|
||||||
final InputStream is = new FileInputStream( file.getAbsolutePath() );
|
return enhancer.enhance( javaClassFile );
|
||||||
|
|
||||||
try {
|
|
||||||
return classPool.makeClass( is );
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new GradleException( "Javassist unable to load class in preparation for enhancing : " + file.getAbsolutePath(), e );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
try {
|
|
||||||
is.close();
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
logger.info( "Was unable to close InputStream : " + file.getAbsolutePath(), e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException e) {
|
|
||||||
// should never happen, but...
|
|
||||||
throw new GradleException( "Unable to locate class file for InputStream: " + file.getAbsolutePath(), e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] doEnhancement(CtClass ctClass, Enhancer enhancer) {
|
|
||||||
try {
|
|
||||||
return enhancer.enhance( ctClass.getName(), ctClass.toBytecode() );
|
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new GradleException( "Unable to enhance class : " + ctClass.getName(), e );
|
throw new GradleException( "Unable to enhance class : " + javaClassFile, e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeOutEnhancedClass(byte[] enhancedBytecode, CtClass ctClass, File file) {
|
private void writeOutEnhancedClass(byte[] enhancedBytecode, File file) {
|
||||||
try {
|
try {
|
||||||
if ( file.delete() ) {
|
if ( file.delete() ) {
|
||||||
if ( !file.createNewFile() ) {
|
if ( !file.createNewFile() ) {
|
||||||
logger.error( "Unable to recreate class file [" + ctClass.getName() + "]" );
|
logger.error( "Unable to recreate class file [" + file.getName() + "]" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.error( "Unable to delete class file [" + ctClass.getName() + "]" );
|
logger.error( "Unable to delete class file [" + file.getName() + "]" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
logger.warn( "Problem preparing class file for writing out enhancements [" + ctClass.getName() + "]" );
|
logger.warn( "Problem preparing class file for writing out enhancements [" + file.getName() + "]" );
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -220,12 +186,11 @@ public class HibernatePlugin implements Plugin<Project> {
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new GradleException( "Error writing to enhanced class [" + ctClass.getName() + "] to file [" + file.getAbsolutePath() + "]", e );
|
throw new GradleException( "Error writing to enhanced class [" + file.getName() + "] to file [" + file.getAbsolutePath() + "]", e );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
ctClass.detach();
|
|
||||||
}
|
}
|
||||||
catch (IOException ignore) {
|
catch (IOException ignore) {
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue