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