HHH-11152: Added BytecodeProvider based on Byte Buddy

This commit is contained in:
Rafael Winterhalter 2016-10-04 22:43:27 +02:00 committed by Steve Ebersole
parent 31a60b84c6
commit ba3359fe62
77 changed files with 4250 additions and 625 deletions

View File

@ -154,6 +154,7 @@ subprojects { subProject ->
testRuntime( libraries.log4j )
testRuntime( libraries.javassist )
testRuntime( libraries.byteBuddy )
testRuntime( libraries.woodstox )
//Databases

View File

@ -76,7 +76,7 @@
org.hibernate.cfg,
org.hibernate.service,
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,
javassist.util.proxy,
*

View File

@ -23,6 +23,7 @@ configurations {
dependencies {
compile( libraries.jpa )
compile( libraries.javassist )
compile( libraries.byteBuddy )
compile( libraries.antlr )
compile( libraries.jta )
compile( libraries.jandex )
@ -95,6 +96,7 @@ dependencies {
testRuntime( libraries.expression_language_impl )
testRuntime( 'jaxen:jaxen:1.1' )
testRuntime( libraries.javassist )
testRuntime( libraries.byteBuddy )
testCompile( project( ':hibernate-jpamodelgen' ) )

View File

@ -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 );
}
}
}

View File

@ -0,0 +1,79 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.bytecode.enhance.internal.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 ) );
}
}

View File

@ -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 {
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View File

@ -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
);
}
}
}

View File

@ -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
);
}
}
}

View File

@ -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() );
}
}

View File

@ -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() );
}
}
}

View File

@ -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 );
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,11 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
/**
* package containing bytecode enhancement code (internals)
*/
package org.hibernate.bytecode.enhance.internal.bytebuddy;

View File

@ -4,7 +4,7 @@
* 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;
package org.hibernate.bytecode.enhance.internal.javassist;
import java.util.Collection;
import java.util.Locale;
@ -37,7 +37,7 @@ public abstract class AttributeTypeDescriptor {
public abstract String buildWriteInterceptionBodyFragment(String fieldName);
public String buildInLineDirtyCheckingBodyFragment(EnhancementContext context, CtField currentValue) {
public String buildInLineDirtyCheckingBodyFragment(JavassistEnhancementContext context, CtField currentValue) {
StringBuilder builder = new StringBuilder();
try {
// should ignore primary keys

View File

@ -4,10 +4,11 @@
* 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;
package org.hibernate.bytecode.enhance.internal.javassist;
import javassist.CannotCompileException;
import javassist.CtClass;
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
@ -22,7 +23,7 @@ import org.hibernate.engine.spi.ManagedComposite;
*/
public class CompositeEnhancer extends PersistentAttributesEnhancer {
public CompositeEnhancer(EnhancementContext context) {
public CompositeEnhancer(JavassistEnhancementContext context) {
super( context );
}
@ -57,10 +58,10 @@ public class CompositeEnhancer extends PersistentAttributesEnhancer {
" if (%2$s == null) { %2$s = new %4$s(); }%n" +
" %2$s.add(name, tracker);%n" +
"}",
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME,
CompositeOwner.class.getName(),
CompositeOwnerTracker.class.getName() );
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME,
CompositeOwner.class.getName(),
CompositeOwnerTracker.class.getName() );
MethodWriter.write( managedCtClass, "" +
"public void %1$s(String name) {%n" +

View File

@ -0,0 +1,224 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.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
);
}
}

View File

@ -4,12 +4,11 @@
* 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;
package org.hibernate.bytecode.enhance.internal.javassist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -20,6 +19,7 @@ import javassist.CtField;
import javassist.Modifier;
import javassist.NotFoundException;
import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker;
import org.hibernate.bytecode.enhance.internal.tracker.SimpleCollectionTracker;
import org.hibernate.bytecode.enhance.internal.tracker.SimpleFieldTracker;
@ -40,7 +40,7 @@ import org.hibernate.engine.spi.SelfDirtinessTracker;
*/
public class EntityEnhancer extends PersistentAttributesEnhancer {
public EntityEnhancer(EnhancementContext context) {
public EntityEnhancer(JavassistEnhancementContext context) {
super( context );
}

View File

@ -4,7 +4,7 @@
* 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;
package org.hibernate.bytecode.enhance.internal.javassist;
import javax.persistence.Transient;

View File

@ -0,0 +1,79 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.bytecode.enhance.internal.javassist;
import javassist.CtClass;
import javassist.CtField;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.UnloadedField;
public class JavassistEnhancementContext {
private final EnhancementContext enhancementContext;
public JavassistEnhancementContext(EnhancementContext enhancementContext) {
this.enhancementContext = enhancementContext;
}
public ClassLoader getLoadingClassLoader() {
return enhancementContext.getLoadingClassLoader();
}
public boolean isEntityClass(CtClass classDescriptor) {
return enhancementContext.isEntityClass( new UnloadedCtClass( classDescriptor ) );
}
public boolean isCompositeClass(CtClass classDescriptor) {
return enhancementContext.isCompositeClass( new UnloadedCtClass( classDescriptor ) );
}
public boolean isMappedSuperclassClass(CtClass classDescriptor) {
return enhancementContext.isMappedSuperclassClass( new UnloadedCtClass( classDescriptor ) );
}
public boolean doBiDirectionalAssociationManagement(CtField field) {
return enhancementContext.doBiDirectionalAssociationManagement( new UnloadedCtField( field ) );
}
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
return enhancementContext.doDirtyCheckingInline( new UnloadedCtClass( classDescriptor ) );
}
public boolean doExtendedEnhancement(CtClass classDescriptor) {
return enhancementContext.doExtendedEnhancement( new UnloadedCtClass( classDescriptor ) );
}
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
return enhancementContext.hasLazyLoadableAttributes( new UnloadedCtClass( classDescriptor ) );
}
public boolean isPersistentField(CtField ctField) {
return enhancementContext.isPersistentField( new UnloadedCtField( ctField ) );
}
public CtField[] order(CtField[] persistentFields) {
UnloadedField[] unloadedFields = new UnloadedField[persistentFields.length];
for ( int i = 0; i < unloadedFields.length; i++ ) {
unloadedFields[i] = new UnloadedCtField( persistentFields[i] );
}
UnloadedField[] ordered = enhancementContext.order( unloadedFields );
CtField[] orderedFields = new CtField[persistentFields.length];
for ( int i = 0; i < orderedFields.length; i++ ) {
orderedFields[i] = ( (UnloadedCtField) ordered[i] ).ctField;
}
return orderedFields;
}
public boolean isLazyLoadable(CtField field) {
return enhancementContext.isLazyLoadable( new UnloadedCtField( field ) );
}
public boolean isMappedCollection(CtField field) {
return enhancementContext.isMappedCollection( new UnloadedCtField( field ) );
}
}

View File

@ -4,11 +4,12 @@
* 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;
package org.hibernate.bytecode.enhance.internal.javassist;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.EnhancerConstants;
import org.hibernate.engine.spi.ManagedMappedSuperclass;
@ -20,7 +21,7 @@ import org.hibernate.engine.spi.ManagedMappedSuperclass;
*/
public class MappedSuperclassEnhancer extends PersistentAttributesEnhancer {
public MappedSuperclassEnhancer(EnhancementContext context) {
public MappedSuperclassEnhancer(JavassistEnhancementContext context) {
super( context );
}

View File

@ -4,7 +4,7 @@
* 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;
package org.hibernate.bytecode.enhance.internal.javassist;
import javassist.CannotCompileException;
import javassist.CtClass;

View File

@ -4,14 +4,13 @@
* 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;
package org.hibernate.bytecode.enhance.internal.javassist;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.persistence.Embedded;
@ -36,9 +35,7 @@ import javassist.bytecode.Opcode;
import javassist.bytecode.stackmap.MapMaker;
import org.hibernate.Hibernate;
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.CompositeOwner;
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>
*/
public class PersistentAttributesEnhancer extends Enhancer {
public class PersistentAttributesEnhancer extends EnhancerImpl {
private static final CoreMessageLogger log = CoreLogging.messageLogger( PersistentAttributesEnhancer.class );
public PersistentAttributesEnhancer(EnhancementContext context) {
public PersistentAttributesEnhancer(JavassistEnhancementContext context) {
super( context );
}
@ -97,7 +94,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
}
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;
}
@ -131,7 +128,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
}
}
private PersistentAttributeAccessMethods enhancePersistentAttribute( CtClass managedCtClass, CtField persistentField) {
private PersistentAttributeAccessMethods enhancePersistentAttribute(CtClass managedCtClass, CtField persistentField) {
try {
AttributeTypeDescriptor typeDescriptor = AttributeTypeDescriptor.resolve( managedCtClass, persistentField );
return new PersistentAttributeAccessMethods(
@ -165,13 +162,13 @@ public class PersistentAttributesEnhancer extends Enhancer {
String declaredReadFragment = "this." + fieldName + "";
String superReadFragment = "super." + readerName + "()";
if (!declared) {
if ( !declared ) {
// create a temporary getter on the supper entity to be able to compile our code
try {
persistentField.getDeclaringClass().getDeclaredMethod( readerName );
persistentField.getDeclaringClass().getDeclaredMethod( writerName );
}
catch (NotFoundException nfe){
catch ( NotFoundException nfe ) {
tmpSuperReader = MethodWriter.addGetter( persistentField.getDeclaringClass(), persistentField.getName(), readerName );
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;
// so if the field is not enabled as lazy-loadable return a plain simple getter as the reader
if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass )
|| !enhancementContext.isLazyLoadable( persistentField ) ) {
if ( !enhancementContext.hasLazyLoadableAttributes( managedCtClass ) || !enhancementContext.isLazyLoadable( persistentField ) ) {
reader = MethodWriter.write(
managedCtClass, "public %s %s() { return %s;%n}",
persistentField.getType().getName(),
@ -237,15 +233,15 @@ public class PersistentAttributesEnhancer extends Enhancer {
try {
boolean declared = persistentField.getDeclaringClass().equals( managedCtClass );
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
try {
persistentField.getDeclaringClass().getDeclaredMethod( readerName );
persistentField.getDeclaringClass().getDeclaredMethod( writerName );
}
catch (NotFoundException nfe){
catch ( NotFoundException nfe ) {
tmpSuperReader = MethodWriter.addGetter( persistentField.getDeclaringClass(), persistentField.getName(), readerName );
tmpSuperWriter = MethodWriter.addSetter( persistentField.getDeclaringClass(), persistentField.getName(), writerName );
}
@ -352,7 +348,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
getter = targetEntity.getDeclaredMethod( mappedByGetterName );
setter = targetEntity.getDeclaredMethod( mappedByGetterName );
}
catch (NotFoundException nfe){
catch ( NotFoundException nfe ) {
getter = MethodWriter.addGetter( targetEntity, mappedBy, mappedByGetterName );
setter = MethodWriter.addSetter( targetEntity, mappedBy, mappedBySetterName );
tmpTargetMethods = true;
@ -397,19 +393,19 @@ public class PersistentAttributesEnhancer extends Enhancer {
}
if ( PersistentAttributesHelper.hasAnnotation( persistentField, OneToMany.class ) ) {
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
// don't use iterator to avoid ConcurrentModException
fieldWriter.insertBefore(
String.format(
" if (this.%3$s != null && %1$s) {%n" +
" Object[] array = this.%3$s.%2$s;%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %4$s target = (%4$s) array[i];%n" +
" if ($1 == null || !$1.contains(target)) { target.%5$s(null); }%n" +
" }%n" +
" }%n",
" Object[] array = this.%3$s.%2$s;%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %4$s target = (%4$s) array[i];%n" +
" if ($1 == null || !$1.contains(target)) { target.%5$s(null); }%n" +
" }%n" +
" }%n",
currentAssociationLoaded,
toArrayMethod,
persistentField.getName(),
@ -420,12 +416,12 @@ public class PersistentAttributesEnhancer extends Enhancer {
fieldWriter.insertAfter(
String.format(
" if ($1 != null && %1$s) {%n" +
" Object[] array = $1.%2$s;%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %4$s target = (%4$s) array[i];%n" +
" if (%3$s && target.%5$s() != this) { target.%6$s(this); }%n" +
" }%n" +
" }%n",
" Object[] array = $1.%2$s;%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %4$s target = (%4$s) array[i];%n" +
" if (%3$s && target.%5$s() != this) { target.%6$s(this); }%n" +
" }%n" +
" }%n",
newAssociationLoaded,
toArrayMethod,
targetElementLoaded,
@ -448,9 +444,9 @@ public class PersistentAttributesEnhancer extends Enhancer {
fieldWriter.insertAfter(
String.format(
" if ($1 != null && %s) {%n" +
" java.util.Collection c = $1.%s();%n" +
" if (c != null && !c.contains(this)) { c.add(this); }%n" +
" }%n",
" java.util.Collection c = $1.%s();%n" +
" if (c != null && !c.contains(this)) { c.add(this); }%n" +
" }%n",
newAssociationLoaded,
mappedByGetterName
)
@ -458,7 +454,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
}
if ( PersistentAttributesHelper.hasAnnotation( persistentField, ManyToMany.class ) ) {
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(
"Bi-directional association for field [%s#%s] not managed: @ManyToMany in java.util.Map attribute not supported ",
managedCtClass.getName(),
@ -469,12 +465,12 @@ public class PersistentAttributesEnhancer extends Enhancer {
fieldWriter.insertBefore(
String.format(
" if (this.%2$s != null && %1$s) {%n" +
" Object[] array = this.%2$s.toArray();%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %3$s target = (%3$s) array[i];%n" +
" if ($1 == null || !$1.contains(target)) { target.%4$s().remove(this); }%n" +
" }%n" +
" }%n",
" Object[] array = this.%2$s.toArray();%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %3$s target = (%3$s) array[i];%n" +
" if ($1 == null || !$1.contains(target)) { target.%4$s().remove(this); }%n" +
" }%n" +
" }%n",
currentAssociationLoaded,
persistentField.getName(),
targetEntity.getName(),
@ -484,15 +480,15 @@ public class PersistentAttributesEnhancer extends Enhancer {
fieldWriter.insertAfter(
String.format(
" if ($1 != null && %s) {%n" +
" Object[] array = $1.toArray();%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %s target = (%s) array[i];%n" +
" if (%s) {%n" +
" java.util.Collection c = target.%s();%n" +
" if (c != this && c != null) { c.add(this); }%n" +
" }%n" +
" }%n" +
" }%n",
" Object[] array = $1.toArray();%n" +
" for (int i = 0; i < array.length; i++) {%n" +
" %s target = (%s) array[i];%n" +
" if (%s) {%n" +
" java.util.Collection c = target.%s();%n" +
" if (c != this && c != null) { c.add(this); }%n" +
" }%n" +
" }%n" +
" }%n",
newAssociationLoaded,
targetEntity.getName(),
targetEntity.getName(),
@ -523,7 +519,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
// cleanup previous owner
fieldWriter.insertBefore(
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(),
CompositeTracker.class.getName(),
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER
@ -533,7 +529,7 @@ public class PersistentAttributesEnhancer extends Enhancer {
// trigger track changes
fieldWriter.insertAfter(
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\");",
persistentField.getName(),
CompositeTracker.class.getName(),
@ -691,11 +687,13 @@ public class PersistentAttributesEnhancer extends Enhancer {
continue;
}
log.debugf( "Extended enhancement: Transforming access to field [%s.%s] from method [%s#%s]",
fieldClassName,
fieldName,
aCtClass.getName(),
methodName );
log.debugf(
"Extended enhancement: Transforming access to field [%s.%s] from method [%s#%s]",
fieldClassName,
fieldName,
aCtClass.getName(),
methodName
);
if ( op == Opcode.GETFIELD ) {
int fieldReaderMethodIndex = constPool.addMethodrefInfo(

View File

@ -4,7 +4,7 @@
* 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;
package org.hibernate.bytecode.enhance.internal.javassist;
import java.beans.Introspector;
import java.lang.annotation.Annotation;
@ -207,7 +207,7 @@ public class PersistentAttributesHelper {
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 );
return local.isEmpty() ? getMappedByFromTargetEntity( persistentField, targetEntity, context ) : local;
}
@ -233,7 +233,7 @@ public class PersistentAttributesHelper {
private static String getMappedByFromTargetEntity(
CtField persistentField,
CtClass targetEntity,
EnhancementContext context) throws NotFoundException {
JavassistEnhancementContext context) throws NotFoundException {
// get mappedBy value by searching in the fields of the target entity class
for ( CtField f : targetEntity.getDeclaredFields() ) {
if ( context.isPersistentField( f )

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.bytecode.enhance.internal.javassist;
import java.lang.annotation.Annotation;
import javassist.CtClass;
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
public class UnloadedCtClass implements UnloadedClass {
private final CtClass ctClass;
public UnloadedCtClass(CtClass ctClass) {
this.ctClass = ctClass;
}
@Override
public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
return ctClass.hasAnnotation( annotationType );
}
@Override
public String getName() {
return ctClass.getName();
}
}

View File

@ -0,0 +1,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 );
}
}

View File

@ -0,0 +1,11 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
/**
* package containing bytecode enhancement code (internals)
*/
package org.hibernate.bytecode.enhance.internal.javassist;

View File

@ -6,6 +6,6 @@
*/
/**
* package containing bytecode enhancement code (internals)
* package containing bytecode enhancement (internals)
*/
package org.hibernate.bytecode.enhance.internal;

View File

@ -14,9 +14,6 @@ import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import javassist.CtClass;
import javassist.CtField;
/**
* 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
*/
@Override
public ClassLoader getLoadingClassLoader() {
return getClass().getClassLoader();
}
@ -34,77 +32,88 @@ public class DefaultEnhancementContext implements EnhancementContext {
/**
* look for @Entity annotation
*/
public boolean isEntityClass(CtClass classDescriptor) {
@Override
public boolean isEntityClass(UnloadedClass classDescriptor) {
return classDescriptor.hasAnnotation( Entity.class );
}
/**
* look for @Embeddable annotation
*/
public boolean isCompositeClass(CtClass classDescriptor) {
@Override
public boolean isCompositeClass(UnloadedClass classDescriptor) {
return classDescriptor.hasAnnotation( Embeddable.class );
}
/**
* look for @MappedSuperclass annotation
*/
public boolean isMappedSuperclassClass(CtClass classDescriptor) {
@Override
public boolean isMappedSuperclassClass(UnloadedClass classDescriptor) {
return classDescriptor.hasAnnotation( MappedSuperclass.class );
}
/**
* @return true
*/
public boolean doBiDirectionalAssociationManagement(CtField field) {
@Override
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
return true;
}
/**
* @return true
*/
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
@Override
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
return true;
}
/**
* @return false
*/
public boolean doExtendedEnhancement(CtClass classDescriptor) {
@Override
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
return false;
}
/**
* @return true
*/
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
@Override
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
return true;
}
/**
* @return true
*/
public boolean isLazyLoadable(CtField field) {
@Override
public boolean isLazyLoadable(UnloadedField field) {
return true;
}
/**
* look for @Transient annotation
*/
public boolean isPersistentField(CtField ctField) {
@Override
public boolean isPersistentField(UnloadedField ctField) {
return ! ctField.hasAnnotation( Transient.class );
}
/**
* 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 );
}
/**
* keep the same order.
*/
public CtField[] order(CtField[] persistentFields) {
@Override
public UnloadedField[] order(UnloadedField[] persistentFields) {
return persistentFields;
}
}

View File

@ -6,9 +6,6 @@
*/
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>
* <li>Build time, via Ant</li>
@ -22,7 +19,6 @@ import javassist.CtField;
* the enhancement is being performed.
*
* @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 {
/**
@ -41,7 +37,7 @@ public interface EnhancementContext {
*
* @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?
@ -50,7 +46,7 @@ public interface EnhancementContext {
*
* @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?
@ -59,7 +55,7 @@ public interface EnhancementContext {
*
* @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?
@ -70,7 +66,7 @@ public interface EnhancementContext {
* the association is managed, i.e. the associations are automatically set; {@code false} indicates that
* 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?
@ -80,7 +76,7 @@ public interface EnhancementContext {
* @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.
*/
public boolean doDirtyCheckingInline(CtClass classDescriptor);
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor);
/**
* 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
* getter / setter method.
*/
public boolean doExtendedEnhancement(CtClass classDescriptor);
public boolean doExtendedEnhancement(UnloadedClass classDescriptor);
/**
* Does the given class define any lazy loadable attributes?
@ -99,7 +95,7 @@ public interface EnhancementContext {
*
* @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
@ -113,7 +109,7 @@ public interface EnhancementContext {
*
* @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
@ -123,7 +119,7 @@ public interface EnhancementContext {
*
* @return The ordered references.
*/
public CtField[] order(CtField[] persistentFields);
public UnloadedField[] order(UnloadedField[] persistentFields);
/**
* 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.
*/
public boolean isLazyLoadable(CtField field);
public boolean isLazyLoadable(UnloadedField field);
/**
* @param field the field to check
*
* @return {@code true} if the field is mapped
*/
public boolean isMappedCollection(CtField field);
public boolean isMappedCollection(UnloadedField field);
}

View File

@ -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 );
}
}

View File

@ -14,6 +14,11 @@ import org.hibernate.HibernateException;
* @author Steve Ebersole
*/
public class EnhancementException extends HibernateException {
public EnhancementException(String message) {
super( message );
}
/**
* Constructs an EnhancementException
*

View File

@ -6,31 +6,8 @@
*/
package org.hibernate.bytecode.enhance.spi;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
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.
@ -39,22 +16,7 @@ import org.hibernate.internal.CoreMessageLogger;
* @author Jason Greene
* @author Luis Barreiro
*/
public class 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 );
}
public interface Enhancer {
/**
* Performs the enhancement.
@ -62,140 +24,11 @@ public class Enhancer {
* @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.
* @return The enhanced bytecode. If the original bytes are not enhanced, null is returned.
*
* @throws EnhancementException Indicates a problem performing the enhancement
*/
public synchronized 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;
}
}
byte[] enhance(String className, byte[] originalBytes) throws EnhancementException;
private ClassPool buildClassPool(final EnhancementContext 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 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 );
}
byte[] enhance(File javaClassFile) throws EnhancementException, IOException;
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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 );
}
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.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;
}
}

View File

@ -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 );
}
}

View File

@ -0,0 +1,67 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.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;
}
}
}

View File

@ -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 );
}
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.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;
}
}

View File

@ -0,0 +1,11 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
/**
* Byte Buddy support internals
*/
package org.hibernate.bytecode.internal.bytebuddy;

View File

@ -8,6 +8,9 @@ package org.hibernate.bytecode.internal.javassist;
import java.lang.reflect.Modifier;
import org.hibernate.bytecode.enhance.internal.javassist.EnhancerImpl;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
import org.hibernate.bytecode.spi.ReflectionOptimizer;
@ -92,4 +95,8 @@ public class BytecodeProviderImpl implements BytecodeProvider {
return null;
}
@Override
public Enhancer getEnhancer(EnhancementContext enhancementContext) {
return new EnhancerImpl( enhancementContext );
}
}

View File

@ -6,6 +6,9 @@
*/
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.
* <p/>
@ -36,4 +39,13 @@ public interface BytecodeProvider {
* @return The reflection optimization delegate.
*/
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);
}

View File

@ -48,7 +48,7 @@ public class InstrumentedClassLoader extends ClassLoader {
try {
final byte[] originalBytecode = ByteCodeHelper.readByteCode( is );
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
// non-instrumented class
return getParent().loadClass( name );
@ -57,7 +57,7 @@ public class InstrumentedClassLoader extends ClassLoader {
return defineClass( name, transformedBytecode, 0, transformedBytecode.length );
}
}
catch( Throwable t ) {
catch ( Throwable t ) {
throw new ClassNotFoundException( name + " not found", t );
}
}

View File

@ -314,17 +314,20 @@ public final class Environment implements AvailableSettings {
}
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 );
return buildBytecodeProvider( provider );
}
private static BytecodeProvider buildBytecodeProvider(String providerName) {
if ( "bytebuddy".equals( providerName ) ) {
return new org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl();
}
if ( "javassist".equals( providerName ) ) {
return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
}
LOG.unknownBytecodeProvider( providerName );
return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
return new org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl();
}
}

View File

@ -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);
}

View File

@ -467,8 +467,8 @@ public interface CoreMessageLogger extends BasicLogger {
String old,
String name);
@Message(value = "Javassist Enhancement failed: %s", id = 142)
String javassistEnhancementFailed(String entityName);
@Message(value = "Bytecode enhancement failed: %s", id = 142)
String bytecodeEnhancementFailed(String entityName);
@LogMessage(level = WARN)
@Message(value = "%s = false breaks the EJB3 specification", id = 144)
@ -1327,7 +1327,7 @@ public interface CoreMessageLogger extends BasicLogger {
void unexpectedRowCounts();
@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);
@LogMessage(level = WARN)

View File

@ -51,6 +51,8 @@ import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryBuilderImplementor;
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
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.Environment;
import org.hibernate.cfg.beanvalidation.BeanValidationIntegrator;
@ -276,39 +278,39 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
return new DefaultEnhancementContext() {
@Override
public boolean isEntityClass(CtClass classDescriptor) {
public boolean isEntityClass(UnloadedClass classDescriptor) {
return managedResources.getAnnotatedClassNames().contains( classDescriptor.getName() )
&& super.isEntityClass( classDescriptor );
}
@Override
public boolean isCompositeClass(CtClass classDescriptor) {
public boolean isCompositeClass(UnloadedClass classDescriptor) {
return managedResources.getAnnotatedClassNames().contains( classDescriptor.getName() )
&& super.isCompositeClass( classDescriptor );
}
@Override
public boolean doBiDirectionalAssociationManagement(CtField field) {
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
return associationManagementEnabled;
}
@Override
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
return dirtyTrackingEnabled;
}
@Override
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
return lazyInitializationEnabled;
}
@Override
public boolean isLazyLoadable(CtField field) {
public boolean isLazyLoadable(UnloadedField field) {
return lazyInitializationEnabled;
}
@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.
return false;
}

View File

@ -9,12 +9,11 @@ package org.hibernate.jpa.internal.enhance;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import javassist.CtClass;
import javassist.CtField;
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.spi.ClassTransformer;
import org.hibernate.cfg.Environment;
/**
* @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.
try {
Enhancer enhancer = new Enhancer( new EnhancementContextWrapper( enhancementContext, loader ) );
Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( new EnhancementContextWrapper( enhancementContext, loader ) );
return enhancer.enhance( className, classfileBuffer );
}
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 );
}
}
}

View File

@ -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 );
}
}
}
}

View File

@ -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
);
}
}

View File

@ -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()
)
);
}
}
}

View File

@ -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;
}
}

View File

@ -126,8 +126,8 @@ public class JavassistProxyFactory implements ProxyFactory, Serializable {
return proxy;
}
catch (Throwable t) {
LOG.error( LOG.javassistEnhancementFailed( entityName ), t );
throw new HibernateException( LOG.javassistEnhancementFailed( entityName ), t );
LOG.error( LOG.bytecodeEnhancementFailed( entityName ), t );
throw new HibernateException( LOG.bytecodeEnhancementFailed( entityName ), t );
}
}
@ -158,7 +158,7 @@ public class JavassistProxyFactory implements ProxyFactory, Serializable {
return proxy;
}
catch ( Throwable t ) {
final String message = LOG.javassistEnhancementFailed( serializableProxy.getEntityName() );
final String message = LOG.bytecodeEnhancementFailed( serializableProxy.getEntityName() );
LOG.error( message, t );
throw new HibernateException( message, t );
}

View File

@ -14,11 +14,9 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javassist.ClassPool;
import javassist.CtClass;
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.cfg.Environment;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
@ -39,9 +37,7 @@ import org.apache.tools.ant.types.FileSet;
public class EnhancementTask extends Task {
private List<FileSet> filesets = new ArrayList<FileSet>();
// Enhancer also builds CtClass instances. Might make sense to share these (ClassPool).
private final ClassPool classPool = new ClassPool( false );
private final Enhancer enhancer = new Enhancer( new DefaultEnhancementContext() );
private final Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( new DefaultEnhancementContext() );
public void addFileset(FileSet set) {
this.filesets.add( set );
@ -72,8 +68,7 @@ public class EnhancementTask extends Task {
private void processClassFile(File javaClassFile) {
try {
final CtClass ctClass = classPool.makeClass( new FileInputStream( javaClassFile ) );
byte[] result = enhancer.enhance( ctClass.getName(), ctClass.toBytecode() );
byte[] result = enhancer.enhance( javaClassFile );
if ( result != null ) {
writeEnhancedClass( javaClassFile, result );
}

View File

@ -11,10 +11,9 @@ import java.io.InputStream;
import java.lang.instrument.IllegalClassFormatException;
import java.util.List;
import javassist.CtClass;
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.UnloadedClass;
import org.hibernate.jpa.internal.enhance.EnhancingClassTransformerImpl;
/**
@ -31,29 +30,36 @@ public class InstrumentedClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// Do not instrument the following packages
if (name != null
&& (name.startsWith("java.lang.") ||
name.startsWith("java.util.")))
return getParent().loadClass(name);
if ( name != null
&& ( name.startsWith( "java.lang." ) ||
name.startsWith( "java.util." ) ) ) {
return getParent().loadClass( 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 );
}
/**
* Specialized {@link ClassLoader#loadClass(String)} that returns the class
* as a byte array.
*
*
* @param name
*
* @return
*
* @throws ClassNotFoundException
*/
public byte[] loadClassBytes(String name) throws ClassNotFoundException {
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[] originalClass = new byte[0];
int r = 0;
@ -64,13 +70,13 @@ public class InstrumentedClassLoader extends ClassLoader {
throw new ClassNotFoundException( name + " not found", e );
}
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( buffer, 0, temp, originalClass.length, buffer.length );
originalClass = temp;
}
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( buffer, 0, temp, originalClass.length, r );
originalClass = temp;
@ -84,13 +90,20 @@ public class InstrumentedClassLoader extends ClassLoader {
EnhancingClassTransformerImpl t = new EnhancingClassTransformerImpl( getEnhancementContext( getParent(), entities ) );
try {
return t.transform(
byte[] transformed = t.transform(
getParent(),
name,
null,
null,
originalClass
);
if ( transformed == null ) {
return originalClass;
}
else {
return transformed;
}
}
catch (IllegalClassFormatException e) {
throw new ClassNotFoundException( name + " not found", e );
@ -110,12 +123,12 @@ public class InstrumentedClassLoader extends ClassLoader {
}
@Override
public boolean isEntityClass(CtClass classDescriptor) {
public boolean isEntityClass(UnloadedClass classDescriptor) {
return entities.contains( classDescriptor.getName() ) && super.isEntityClass( classDescriptor );
}
@Override
public boolean isCompositeClass(CtClass classDescriptor) {
public boolean isCompositeClass(UnloadedClass classDescriptor) {
return entities.contains( classDescriptor.getName() ) && super.isCompositeClass( classDescriptor );
}
};

View File

@ -6,24 +6,14 @@
*/
package org.hibernate.jpa.test.enhancement;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
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.ManagedComposite;
import org.hibernate.engine.spi.ManagedEntity;
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.Test;
@ -36,10 +26,10 @@ import static org.junit.Assert.assertTrue;
* @author Dustin Schultz
*/
public class InterceptFieldClassFileTransformerTest {
private List<String> entities = new ArrayList<String>();
private InstrumentedClassLoader loader = null;
@Before
public void setup() {
entities.add( Simple.class.getName() );
@ -48,13 +38,13 @@ public class InterceptFieldClassFileTransformerTest {
cl.setEntities( entities );
this.loader = cl;
}
/**
* Tests that class file enhancement works.
*
*
* @throws Exception in case the test fails.
*/
@Test
@Test
public void testEnhancement() throws Exception {
// sanity check that the class is unmodified and does not contain getFieldHandler()
assertFalse( implementsManaged( Simple.class ) );
@ -75,35 +65,4 @@ public class InterceptFieldClassFileTransformerTest {
}
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());
}
}
}
}

View File

@ -75,18 +75,18 @@ public class DuplicatedDiscriminatorValueTest extends BaseUnitTestCase {
@Entity
@DiscriminatorValue(DISCRIMINATOR_VALUE) // Duplicated discriminator value in single hierarchy.
private static class Building1 extends Building {
public static class Building1 extends Building {
}
@Entity
@DiscriminatorValue(DISCRIMINATOR_VALUE) // Duplicated discriminator value in single hierarchy.
private static class Building2 extends Building {
public static class Building2 extends Building {
}
@Entity
@DiscriminatorColumn(name = "entity_type")
@DiscriminatorValue("F")
private static class Furniture {
public static class Furniture {
@Id
@GeneratedValue
private Integer id;
@ -94,6 +94,6 @@ public class DuplicatedDiscriminatorValueTest extends BaseUnitTestCase {
@Entity
@DiscriminatorValue(DISCRIMINATOR_VALUE) // Duplicated discriminator value in different hierarchy.
private static class Chair extends Furniture {
public static class Chair extends Furniture {
}
}

View File

@ -4,40 +4,20 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.bytecode.javassist;
package org.hibernate.test.bytecode;
import java.text.ParseException;
import org.hibernate.Hibernate;
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.test.bytecode.Bean;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
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 static class LocalSkipMatcher implements Skip.Matcher {
@Override
public boolean isMatch() {
return ! BytecodeProviderImpl.class.isInstance( Environment.getBytecodeProvider() );
}
}
@Override
public String[] getMappings() {
return new String[] { "bytecode/Bean.hbm.xml" };

View File

@ -4,15 +4,15 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.bytecode.javassist;
package org.hibernate.test.bytecode;
import org.junit.Test;
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.test.bytecode.Bean;
import org.hibernate.test.bytecode.BeanReflectionHelper;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.junit.Assert.assertEquals;
@ -34,7 +34,7 @@ public class ReflectionOptimizerTest extends BaseUnitTestCase {
@Test
public void testReflectionOptimization() {
BytecodeProviderImpl provider = new BytecodeProviderImpl();
BytecodeProvider provider = Environment.getBytecodeProvider();
ReflectionOptimizer optimizer = provider.getReflectionOptimizer(
Bean.class,
BeanReflectionHelper.getGetterNames(),

View File

@ -6,7 +6,7 @@
*/
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.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.LazyCollectionWithClosedSessionTestTask;
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.junit.Test;
@ -112,7 +113,7 @@ public class EnhancerTest extends BaseUnitTestCase {
public void testLazyProxyOnEnhancedEntity() {
EnhancerTestUtils.runEnhancerTestTask( LazyProxyOnEnhancedEntityTestTask.class, new EnhancerTestContext() {
@Override
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
return false;
}
} );
@ -176,7 +177,7 @@ public class EnhancerTest extends BaseUnitTestCase {
EnhancerTestUtils.runEnhancerTestTask( MappedSuperclassTestTask.class );
EnhancerTestUtils.runEnhancerTestTask( MappedSuperclassTestTask.class, new EnhancerTestContext() {
@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
return false;
}

View File

@ -56,7 +56,7 @@ public class ProxyInterfaceClassLoaderTest extends BaseCoreFunctionalTestCase {
s.close();
}
interface IPerson {
public interface IPerson {
int getId();

View File

@ -5,6 +5,7 @@ dependencies {
compile( libraries.commons_annotations )
compile( libraries.jpa )
compile( libraries.javassist )
compile( libraries.byteBuddy )
compile( libraries.jta )
}

View File

@ -16,6 +16,7 @@ dependencies {
testCompile( project( ':hibernate-testing' ) )
testCompile( project( path: ':hibernate-core', configuration: 'tests' ) )
testRuntime( libraries.javassist )
testRuntime( libraries.byteBuddy )
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Java 9 ftw!

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.envers.internal.tools;
import javassist.util.proxy.ProxyFactory;
import org.hibernate.Session;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
@ -76,7 +75,7 @@ public abstract class EntityTools {
if ( clazz == null ) {
return null;
}
else if ( ProxyFactory.isProxyClass( clazz ) ) {
else if ( HibernateProxy.class.isAssignableFrom( clazz ) ) {
// Get the source class of Javassist proxy instance.
return (Class<T>) clazz.getSuperclass();
}

View File

@ -37,7 +37,8 @@ ext {
minorSlot: minorSlot,
version: rootProject.hibernateTargetVersion,
wildflyVersion: wildflyVersion,
javassistVersion: javassistVersion
javassistVersion: javassistVersion,
byteBuddyVersion: byteBuddyVersion
];
}
@ -54,12 +55,14 @@ configurations {
wildflyDist
javassist
byteBuddy
}
dependencies {
jipijapa "org.wildfly:jipijapa-hibernate5:${wildflyVersion}"
wildflyDist "org.wildfly:wildfly-dist:${wildflyVersion}@zip"
javassist libraries.javassist
byteBuddy libraries.byteBuddy
testCompile project( ":hibernate-core" )
testCompile project( ":hibernate-envers" )
@ -121,6 +124,8 @@ task createModulesZip(type: Zip, dependsOn: [copyAndExpandModuleXml]) {
// also need Javassist's jar
from configurations.javassist
// also need Byte Buddy's jar
from configurations.byteBuddy
}
into( 'org/hibernate/infinispan/' + slot ) {

View File

@ -10,6 +10,7 @@
<resource-root path="hibernate-core-${version}.jar"/>
<resource-root path="hibernate-envers-${version}.jar"/>
<resource-root path="javassist-${javassistVersion}.jar"/>
<resource-root path="byte-buddy-${byteBuddyVersion}.jar"/>
</resources>
<dependencies>

View File

@ -47,6 +47,7 @@ dependencies {
testRuntime('jaxen:jaxen:1.1')
testRuntime(libraries.javassist)
testRuntime(libraries.byteBuddy)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -10,6 +10,8 @@ import javassist.CtClass;
import javassist.CtField;
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
@ -19,27 +21,27 @@ import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
public class EnhancerTestContext extends DefaultEnhancementContext {
@Override
public boolean doBiDirectionalAssociationManagement(CtField field) {
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
return true;
}
@Override
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
return true;
}
@Override
public boolean doExtendedEnhancement(CtClass classDescriptor) {
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
return true;
}
@Override
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
return true;
}
@Override
public boolean isLazyLoadable(CtField field) {
public boolean isLazyLoadable(UnloadedField field) {
return true;
}

View File

@ -23,6 +23,7 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.internal.MutableEntityEntryFactory;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.SelfDirtinessTracker;
@ -57,8 +58,8 @@ public abstract class EnhancerTestUtils extends BaseUnitTestCase {
CtClass entityCtClass = generateCtClassForAnEntity( classToEnhance );
byte[] original = entityCtClass.toBytecode();
byte[] enhanced = new Enhancer( new EnhancerTestContext() ).enhance( entityCtClass.getName(), original );
assertFalse( "entity was not enhanced", Arrays.equals( original, enhanced ) );
byte[] enhanced = Environment.getBytecodeProvider().getEnhancer( new EnhancerTestContext() ).enhance( entityCtClass.getName(), original );
assertFalse( "entity was not enhanced", enhanced == null );
log.infof( "enhanced entity [%s]", entityCtClass.getName() );
ClassPool cp = new ClassPool( false );
@ -127,7 +128,7 @@ public abstract class EnhancerTestUtils extends BaseUnitTestCase {
private static ClassLoader getEnhancerClassLoader(EnhancementContext context, String packageName) {
return new ClassLoader() {
private Enhancer enhancer = new Enhancer( context );
private Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( context );
@SuppressWarnings("ResultOfMethodCallIgnored")
@Override
@ -151,12 +152,17 @@ public abstract class EnhancerTestUtils extends BaseUnitTestCase {
byte[] enhanced = enhancer.enhance( name, original );
File f = new File( workingDir + File.separator + name.replace( ".", File.separator ) + ".class" );
f.getParentFile().mkdirs();
f.createNewFile();
FileOutputStream out = new FileOutputStream( f );
out.write( enhanced );
out.close();
if ( enhanced != null ) {
File f = new File( workingDir + File.separator + name.replace( ".", File.separator ) + ".class" );
f.getParentFile().mkdirs();
f.createNewFile();
FileOutputStream out = new FileOutputStream( f );
out.write( enhanced );
out.close();
}
else {
enhanced = original;
}
return defineClass( name, enhanced, 0, enhanced.length );
}

View File

@ -19,6 +19,7 @@ ext {
cdiVersion = '1.1'
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
wildflyVersion = '10.0.0.Final'
@ -48,6 +49,9 @@ ext {
// Javassist
javassist: "org.javassist:javassist:${javassistVersion}",
// Byte Buddy
byteBuddy: "net.bytebuddy:byte-buddy:${byteBuddyVersion}",
// javax
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

View File

@ -29,6 +29,7 @@ dependencies {
compile( project(':hibernate-core') ) { transitive = false }
compile( libraries.jpa ) { transitive = false }
compile( libraries.javassist ) { transitive = false }
compile( libraries.byteBuddy ) { transitive = false }
compile 'org.codehaus.plexus:plexus-utils:3.0.1'
runtime( libraries.maven_core )
runtime( libraries.maven_artifact )
@ -37,6 +38,7 @@ dependencies {
runtime( project(':hibernate-core') )
runtime( libraries.jpa )
runtime( libraries.javassist )
runtime( libraries.byteBuddy )
runtime 'org.codehaus.plexus:plexus-utils:3.0.1'
}
@ -56,6 +58,7 @@ task processPluginXml(type: Copy) {
+ generateMavenDependency(libraries.jta)\
+ generateMavenDependency(libraries.commons_annotations)\
+ generateMavenDependency(libraries.javassist)\
+ generateMavenDependency(libraries.byteBuddy)\
+ generateMavenDependency(libraries.logging)\
+ generateMavenDependency("org.hibernate:hibernate-core:" + project.version)])
}

View File

@ -8,11 +8,9 @@ package org.hibernate.orm.tooling.maven;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
@ -21,10 +19,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
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.EnhancementContext;
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.
@ -105,27 +102,27 @@ public class MavenEnhancePlugin extends AbstractMojo {
}
@Override
public boolean doBiDirectionalAssociationManagement(CtField field) {
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
return enableAssociationManagement;
}
@Override
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
return enableDirtyTracking;
}
@Override
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
return enableLazyInitialization;
}
@Override
public boolean isLazyLoadable(CtField field) {
public boolean isLazyLoadable(UnloadedField field) {
return enableLazyInitialization;
}
@Override
public boolean doExtendedEnhancement(CtClass classDescriptor) {
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
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." );
}
final Enhancer enhancer = new Enhancer( enhancementContext );
final ClassPool classPool = new ClassPool( false );
final Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( enhancementContext );
for ( File file : sourceSet ) {
final CtClass ctClass = toCtClass( file, classPool );
if ( ctClass == null ) {
final byte[] enhancedBytecode = doEnhancement( file, enhancer );
if ( enhancedBytecode == null ) {
continue;
}
if ( !enableLazyInitialization ) {
if ( !enhancementContext.isEntityClass( ctClass )
&& !enhancementContext.isCompositeClass( ctClass )
&& !enhancementContext.isMappedSuperclassClass( ctClass ) ) {
getLog().info( "Skipping class file [" + file.getAbsolutePath() + "], not an entity nor embeddable" );
continue;
}
}
writeOutEnhancedClass( enhancedBytecode, file );
final byte[] enhancedBytecode = doEnhancement( ctClass, enhancer );
writeOutEnhancedClass( enhancedBytecode, ctClass, file );
getLog().info( "Successfully enhanced class [" + ctClass.getName() + "]" );
getLog().info( "Successfully enhanced class [" + file + "]" );
}
}
@ -204,47 +192,12 @@ public class MavenEnhancePlugin extends AbstractMojo {
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 {
final InputStream is = new FileInputStream( file.getAbsolutePath() );
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() );
return enhancer.enhance(javaClassFile);
}
catch (Exception e) {
String msg = "Unable to enhance class: " + ctClass.getName();
String msg = "Unable to enhance class: " + javaClassFile.getName();
if ( failOnError ) {
throw new MojoExecutionException( msg, e );
}
@ -283,22 +236,19 @@ public class MavenEnhancePlugin extends AbstractMojo {
Collections.addAll( this.sourceSet, files );
}
private void writeOutEnhancedClass(byte[] enhancedBytecode, CtClass ctClass, File file) throws MojoExecutionException{
if ( enhancedBytecode == null ) {
return;
}
private void writeOutEnhancedClass(byte[] enhancedBytecode, File file) throws MojoExecutionException {
try {
if ( file.delete() ) {
if ( !file.createNewFile() ) {
getLog().error( "Unable to recreate class file [" + ctClass.getName() + "]" );
getLog().error( "Unable to recreate class file [" + file.getName() + "]" );
}
}
else {
getLog().error( "Unable to delete class file [" + ctClass.getName() + "]" );
getLog().error( "Unable to delete class file [" + file.getName() + "]" );
}
}
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 {
@ -308,7 +258,7 @@ public class MavenEnhancePlugin extends AbstractMojo {
outputStream.flush();
}
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 ) {
throw new MojoExecutionException( msg, e );
}
@ -317,7 +267,6 @@ public class MavenEnhancePlugin extends AbstractMojo {
finally {
try {
outputStream.close();
ctClass.detach();
}
catch (IOException ignore) {
}

View File

@ -14,6 +14,7 @@ dependencies {
compile( project( ':hibernate-core' ) )
compile( libraries.jpa )
compile( libraries.javassist )
compile( libraries.byteBuddy )
compile gradleApi()
compile localGroovy()
}

View File

@ -7,21 +7,15 @@
package org.hibernate.orm.tooling.gradle;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import org.gradle.api.Action;
import org.gradle.api.GradleException;
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.EnhancementContext;
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.
@ -92,37 +89,36 @@ public class HibernatePlugin implements Plugin<Project> {
}
@Override
public boolean doBiDirectionalAssociationManagement(CtField field) {
public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
return hibernateExtension.enhance.getEnableAssociationManagement();
}
@Override
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
return hibernateExtension.enhance.getEnableDirtyTracking();
}
@Override
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
return hibernateExtension.enhance.getEnableLazyInitialization();
}
@Override
public boolean isLazyLoadable(CtField field) {
public boolean isLazyLoadable(UnloadedField field) {
return hibernateExtension.enhance.getEnableLazyInitialization();
}
@Override
public boolean doExtendedEnhancement(CtClass classDescriptor) {
public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
return 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 ClassPool classPool = new ClassPool( false );
final Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( enhancementContext );
final FileTree fileTree = project.fileTree( sourceSet.getOutput().getClassesDir() );
for ( File file : fileTree ) {
@ -130,19 +126,14 @@ public class HibernatePlugin implements Plugin<Project> {
continue;
}
final CtClass ctClass = toCtClass( file, classPool );
if ( !enhancementContext.isEntityClass( ctClass )
&& !enhancementContext.isCompositeClass( ctClass )
&& !enhancementContext.isMappedSuperclassClass( ctClass ) ) {
logger.info( "Skipping class [" + file.getAbsolutePath() + "], not an entity nor embeddable" );
continue;
final byte[] enhancedBytecode = doEnhancement( file, enhancer );
if ( enhancedBytecode != null ) {
writeOutEnhancedClass( enhancedBytecode, file );
logger.info( "Successfully enhanced class [" + file + "]" );
}
else {
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() );
}
private CtClass toCtClass(File file, ClassPool classPool) {
private byte[] doEnhancement(File javaClassFile, Enhancer enhancer) {
try {
final InputStream is = new FileInputStream( file.getAbsolutePath() );
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() );
return enhancer.enhance( javaClassFile );
}
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 {
if ( file.delete() ) {
if ( !file.createNewFile() ) {
logger.error( "Unable to recreate class file [" + ctClass.getName() + "]" );
logger.error( "Unable to recreate class file [" + file.getName() + "]" );
}
}
else {
logger.error( "Unable to delete class file [" + ctClass.getName() + "]" );
logger.error( "Unable to delete class file [" + file.getName() + "]" );
}
}
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 {
@ -220,12 +186,11 @@ public class HibernatePlugin implements Plugin<Project> {
outputStream.flush();
}
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 {
try {
outputStream.close();
ctClass.detach();
}
catch (IOException ignore) {
}