All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.bytecode.enhance.internal.javassist.EnhancerImpl Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * 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
		);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy