HHH-8354 added new dirty-checking options to newly cherry-picked plugins

This commit is contained in:
Brett Meyer 2013-11-21 15:35:24 -05:00
parent a0e242992b
commit 492ea5d2a5
2 changed files with 159 additions and 115 deletions

View File

@ -29,7 +29,6 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -37,17 +36,20 @@ import javassist.ClassPool;
import javassist.CtClass; import javassist.CtClass;
import javassist.CtField; import javassist.CtField;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Transient; import javax.persistence.Transient;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.Parameter;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer;
/** /**
* This plugin will enhance Entity objects. * This plugin will enhance Entity objects.
@ -55,13 +57,14 @@ import org.apache.maven.plugins.annotations.Parameter;
* @author Jeremy Whiting * @author Jeremy Whiting
*/ */
@Mojo(name = "enhance") @Mojo(name = "enhance")
public class MavenEnhancePlugin extends AbstractMojo { public class MavenEnhancePlugin extends AbstractMojo implements EnhancementContext {
/** /**
* The contexts to use during enhancement. * The contexts to use during enhancement.
*/ */
private List<File> classes = new ArrayList<File>(); private List<File> classes = new ArrayList<File>();
private ClassPool pool = new ClassPool( false ); private ClassPool pool = new ClassPool( false );
private final Enhancer enhancer = new Enhancer( this);
private static final String CLASS_EXTENSION = ".class"; private static final String CLASS_EXTENSION = ".class";
@ -74,57 +77,9 @@ public class MavenEnhancePlugin extends AbstractMojo {
File root = new File( this.dir ); File root = new File( this.dir );
walkDir( root ); walkDir( root );
Enhancer enhancer = new Enhancer( new EnhancementContext() {
private ClassLoader overridden;
public ClassLoader getLoadingClassLoader() {
if ( null == this.overridden ) {
return getClass().getClassLoader();
}
else {
return this.overridden;
}
}
public void setClassLoader(ClassLoader loader) {
this.overridden = loader;
}
public boolean isEntityClass(CtClass classDescriptor) {
return true;
}
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
return true;
}
public boolean isLazyLoadable(CtField field) {
return true;
}
public boolean isCompositeClass(CtClass classDescriptor) {
return false;
}
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
return false;
}
public CtField[] order(CtField[] fields) {
// TODO: load ordering from configuration.
return fields;
}
public boolean isPersistentField(CtField ctField) {
return !ctField.hasAnnotation( Transient.class );
}
} );
if ( 0 < classes.size() ) { if ( 0 < classes.size() ) {
for ( File file : classes ) { for ( File file : classes ) {
enhanceClass( enhancer, file ); processClassFile(file);
} }
} }
@ -152,6 +107,7 @@ public class MavenEnhancePlugin extends AbstractMojo {
} }
private void walkDir(File dir, FileFilter classesFilter, FileFilter dirFilter) { private void walkDir(File dir, FileFilter classesFilter, FileFilter dirFilter) {
File[] dirs = dir.listFiles( dirFilter ); File[] dirs = dir.listFiles( dirFilter );
for ( int i = 0; i < dirs.length; i++ ) { for ( int i = 0; i < dirs.length; i++ ) {
walkDir( dirs[i], classesFilter, dirFilter ); walkDir( dirs[i], classesFilter, dirFilter );
@ -163,67 +119,82 @@ public class MavenEnhancePlugin extends AbstractMojo {
} }
} }
private void enhanceClass(Enhancer enhancer, File file) {
byte[] enhancedBytecode = null; /**
InputStream is = null; * Atm only process files annotated with either @Entity or @Embeddable
CtClass clas = null; * @param javaClassFile
*/
private void processClassFile(File javaClassFile)
throws MojoExecutionException {
try { try {
is = new FileInputStream( file.toString() ); final CtClass ctClass = getClassPool().makeClass( new FileInputStream( javaClassFile ) );
clas = getClassPool().makeClass( is ); if(this.isEntityClass(ctClass))
if ( !clas.hasAnnotation( Entity.class ) ) { processEntityClassFile(javaClassFile, ctClass);
getLog().debug( "Class $file not an annotated Entity class. skipping..." ); else if(this.isCompositeClass(ctClass))
} processCompositeClassFile(javaClassFile, ctClass);
else {
enhancedBytecode = enhancer.enhance( clas.getName(), clas.toBytecode() );
}
}
catch (Exception e) {
getLog().error( "Unable to enhance class [${file.toString()}]", e );
return;
}
finally {
try {
if ( null != is )
is.close();
}
catch (IOException ioe) {}
}
if ( null != enhancedBytecode ) {
if ( file.delete() ) {
try {
if ( !file.createNewFile() ) {
getLog().error( "Unable to recreate class file [" + clas.getName() + "]" );
}
}
catch (IOException ioe) {
}
}
else {
getLog().error( "Unable to delete class file [" + clas.getName() + "]" );
}
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream( file, false );
outputStream.write( enhancedBytecode );
outputStream.flush();
}
catch (IOException ioe) {
}
finally {
try {
if ( outputStream != null )
outputStream.close();
clas.detach();// release memory
}
catch (IOException ignore) {
}
} }
catch (IOException e) {
throw new MojoExecutionException(
String.format( "Error processing included file [%s]", javaClassFile.getAbsolutePath() ), e );
} }
} }
public void setDir(String dir) { private void processEntityClassFile(File javaClassFile, CtClass ctClass ) {
if ( null != dir && !"".equals( dir.trim() ) ) { try {
this.dir = dir; byte[] result = enhancer.enhance( ctClass.getName(), ctClass.toBytecode() );
if(result != null)
writeEnhancedClass(javaClassFile, result);
}
catch (Exception e) {
getLog().error( "Unable to enhance class [" + ctClass.getName() + "]", e);
return;
}
}
private void processCompositeClassFile(File javaClassFile, CtClass ctClass) {
try {
byte[] result = enhancer.enhanceComposite(ctClass.getName(), ctClass.toBytecode());
if(result != null)
writeEnhancedClass(javaClassFile, result);
}
catch (Exception e) {
getLog().error( "Unable to enhance class [" + ctClass.getName() + "]", e);
return;
}
}
private void writeEnhancedClass(File javaClassFile, byte[] result)
throws MojoExecutionException {
try {
if ( javaClassFile.delete() ) {
if ( ! javaClassFile.createNewFile() ) {
getLog().error( "Unable to recreate class file [" + javaClassFile.getName() + "]");
}
}
else {
getLog().error( "Unable to delete class file [" + javaClassFile.getName() + "]");
}
FileOutputStream outputStream = new FileOutputStream( javaClassFile, false );
try {
outputStream.write( result);
outputStream.flush();
}
finally {
try {
outputStream.close();
}
catch ( IOException ignore) {
}
}
}
catch (FileNotFoundException ignore) {
// should not ever happen because of explicit checks
}
catch (IOException e) {
throw new MojoExecutionException(
String.format( "Error processing included file [%s]", javaClassFile.getAbsolutePath() ), e );
} }
} }
@ -231,4 +202,63 @@ public class MavenEnhancePlugin extends AbstractMojo {
return this.pool; return this.pool;
} }
private boolean shouldInclude(CtClass ctClass) {
// we currently only handle entity enhancement
return ctClass.hasAnnotation( Entity.class );
}
@Override
public ClassLoader getLoadingClassLoader() {
return getClass().getClassLoader();
}
@Override
public boolean isEntityClass(CtClass classDescriptor) {
return classDescriptor.hasAnnotation(Entity.class);
}
@Override
public boolean isCompositeClass(CtClass classDescriptor) {
return classDescriptor.hasAnnotation(Embeddable.class);
}
@Override
public boolean doDirtyCheckingInline(CtClass classDescriptor) {
return true;
}
@Override
public boolean hasLazyLoadableAttributes(CtClass classDescriptor) {
return true;
}
@Override
public boolean isLazyLoadable(CtField field) {
return true;
}
@Override
public boolean isPersistentField(CtField ctField) {
// current check is to look for @Transient
return ! ctField.hasAnnotation( Transient.class );
}
@Override
public boolean isMappedCollection(CtField field) {
try {
return (field.getAnnotation(OneToMany.class) != null ||
field.getAnnotation(ManyToMany.class) != null ||
field.getAnnotation(ElementCollection.class) != null);
}
catch (ClassNotFoundException e) {
return false;
}
}
@Override
public CtField[] order(CtField[] persistentFields) {
// for now...
return persistentFields;
// eventually needs to consult the Hibernate metamodel for proper ordering
}
} }

View File

@ -23,19 +23,22 @@
*/ */
package org.hibernate.bytecode.enhance.plugins package org.hibernate.bytecode.enhance.plugins
import org.hibernate.bytecode.enhance.spi.Enhancer
import org.hibernate.bytecode.enhance.spi.EnhancementContext
import javassist.ClassPool import javassist.ClassPool
import javassist.CtClass import javassist.CtClass
import javassist.CtField import javassist.CtField
import javax.persistence.Transient
import javax.persistence.ElementCollection
import javax.persistence.Entity import javax.persistence.Entity
import java.io.FileInputStream import javax.persistence.ManyToMany
import java.io.FileOutputStream import javax.persistence.OneToMany
import javax.persistence.Transient
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.file.FileTree
import org.gradle.api.plugins.Convention import org.gradle.api.plugins.Convention
import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskAction
import org.gradle.api.file.FileTree import org.hibernate.bytecode.enhance.spi.EnhancementContext
import org.hibernate.bytecode.enhance.spi.Enhancer
/** /**
* Plugin to enhance Entities using the context(s) to determine * Plugin to enhance Entities using the context(s) to determine
@ -136,7 +139,7 @@ public class EnhanceTask extends DefaultTask implements EnhancementContext {
return false; return false;
} }
public boolean doDirtyCheckingInline(CtClass classDescriptor) { public boolean doDirtyCheckingInline(CtClass classDescriptor) {
return false; return true;
} }
public CtField[] order(CtField[] fields) { public CtField[] order(CtField[] fields) {
@ -144,6 +147,17 @@ public class EnhanceTask extends DefaultTask implements EnhancementContext {
return fields; return fields;
} }
public boolean isMappedCollection(CtField field) {
try {
return (field.getAnnotation(OneToMany.class) != null ||
field.getAnnotation(ManyToMany.class) != null ||
field.getAnnotation(ElementCollection.class) != null);
}
catch (ClassNotFoundException e) {
return false;
}
}
public boolean isPersistentField(CtField ctField) { public boolean isPersistentField(CtField ctField) {
return !ctField.hasAnnotation( Transient.class ); return !ctField.hasAnnotation( Transient.class );
} }