HHH-9822 - Switch runtime JPA Class transformation to use new bytecode Enhancer

This commit is contained in:
Steve Ebersole 2015-07-06 16:28:28 -05:00
parent 99c643febc
commit ae070f32c9
7 changed files with 130 additions and 18 deletions

View File

@ -50,7 +50,7 @@ public class FieldWriter {
private static void addPrivateTransient(CtClass target, CtClass type, String name) {
addWithModifiers( target, type, name, Modifier.PRIVATE | Modifier.TRANSIENT, Transient.class );
log.debugf( "Wrote field into [%s]: @Transient private transient %s %s() %s;%n", target.getName(), type.getName(), name );
log.debugf( "Wrote field into [%s]: @Transient private transient %s %s;%n", target.getName(), type.getName(), name );
}
private static void addWithModifiers(CtClass target, CtClass type, String name, int modifiers, Class<?> ... annotations ) {

View File

@ -16,7 +16,7 @@ import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
import org.hibernate.jpa.internal.instrument.InterceptFieldClassFileTransformer;
import org.hibernate.jpa.internal.enhance.EnhancingClassTransformerImpl;
/**
* @author Steve Ebersole
@ -110,6 +110,6 @@ public class PersistenceUnitInfoDescriptor implements PersistenceUnitDescriptor
@Override
public void pushClassTransformer(Collection<String> entityClassNames) {
persistenceUnitInfo.addTransformer( new InterceptFieldClassFileTransformer( entityClassNames ) );
persistenceUnitInfo.addTransformer( new EnhancingClassTransformerImpl( entityClassNames ) );
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.jpa.internal.enhance;
import java.util.Collection;
import javassist.CtClass;
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
/**
* @author Steve Ebersole
*/
public class EnhancementContextImpl extends DefaultEnhancementContext {
private final Collection<String> classNames;
private final ClassLoader classLoader;
public EnhancementContextImpl(Collection<String> classNames, ClassLoader classLoader) {
this.classNames = classNames;
this.classLoader = classLoader;
}
@Override
public ClassLoader getLoadingClassLoader() {
return classLoader;
}
@Override
public boolean isEntityClass(CtClass classDescriptor) {
return classNames.contains( classDescriptor.getName() )
&& super.isEntityClass( classDescriptor );
}
@Override
public boolean isCompositeClass(CtClass classDescriptor) {
return classNames.contains( classDescriptor.getName() )
&& super.isCompositeClass( classDescriptor );
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.jpa.internal.enhance;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.spi.ClassTransformer;
import org.hibernate.bytecode.enhance.spi.Enhancer;
/**
* @author Steve Ebersole
*/
public class EnhancingClassTransformerImpl implements ClassTransformer {
private final Collection<String> classNames;
private Enhancer enhancer;
public EnhancingClassTransformerImpl(Collection<String> incomingClassNames) {
this.classNames = new ArrayList<String>( incomingClassNames.size() );
this.classNames.addAll( incomingClassNames );
}
@Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if ( enhancer == null ) {
enhancer = new Enhancer( new EnhancementContextImpl( classNames, loader ) );
}
try {
return enhancer.enhance( className, classfileBuffer );
}
catch (final Exception e) {
throw new IllegalClassFormatException( "Error performing enhancement" ) {
@Override
public synchronized Throwable getCause() {
return e;
}
};
}
}
}

View File

@ -13,7 +13,7 @@ import java.io.InputStream;
import java.lang.instrument.IllegalClassFormatException;
import java.util.List;
import org.hibernate.jpa.internal.instrument.InterceptFieldClassFileTransformer;
import org.hibernate.jpa.internal.enhance.EnhancingClassTransformerImpl;
/**
* @author Emmanuel Bernard
@ -79,7 +79,7 @@ public class InstrumentedClassLoader extends ClassLoader {
catch (IOException e) {
throw new ClassNotFoundException( name + " not found", e );
}
InterceptFieldClassFileTransformer t = new InterceptFieldClassFileTransformer( entities );
EnhancingClassTransformerImpl t = new EnhancingClassTransformerImpl( entities );
byte[] transformed = new byte[0];
try {
transformed = t.transform(

View File

@ -20,11 +20,19 @@ 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.testing.TestForIssue;
import org.hibernate.testing.junit4.ExtraAssertions;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Emmanuel Bernard
* @author Hardy Ferentschik
@ -52,21 +60,25 @@ public class InterceptFieldClassFileTransformerTest {
@Test
public void testEnhancement() throws Exception {
// sanity check that the class is unmodified and does not contain getFieldHandler()
try {
Simple.class.getDeclaredMethod( "getFieldHandler" );
Assert.fail();
} catch ( NoSuchMethodException nsme ) {
// success
}
assertFalse( implementsManaged( Simple.class ) );
Class clazz = loader.loadClass( entities.get( 0 ) );
// javassist is our default byte code enhancer. Enhancing will eg add the method getFieldHandler()
// see org.hibernate.bytecode.internal.javassist.FieldTransformer
Method method = clazz.getDeclaredMethod( "getFieldHandler" );
Assert.assertNotNull( method );
// enhancement would have added the ManagedEntity interface...
assertTrue( implementsManaged( clazz ) );
}
private boolean implementsManaged(Class clazz) {
for ( Class intf : clazz.getInterfaces() ) {
if ( Managed.class.getName().equals( intf.getName() )
|| ManagedEntity.class.getName().equals( intf.getName() )
|| ManagedComposite.class.getName().equals( intf.getName() ) ) {
return true;
}
}
return false;
}
/**
* Tests that methods that were enhanced by javassist have
* StackMapTables for java verification. Without these,

View File

@ -10,6 +10,8 @@ package org.hibernate.jpa.test.instrument;
import java.util.Collection;
import javax.persistence.Entity;
import org.hibernate.jpa.internal.instrument.InterceptFieldClassFileTransformer;
@ -19,6 +21,7 @@ import org.hibernate.jpa.internal.instrument.InterceptFieldClassFileTransformer;
* @author Emmanuel Bernard
* @author Dustin Schultz
*/
@Entity
public class Simple {
private String name;