Author: ste...@jb... Date: 2006-08-03 16:35:26 -0400 (Thu, 03 Aug 2006) New Revision: 10209 Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/InstrumentedClassLoader.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldFilter.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldHandled.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldHandler.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldTransformer.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/BasicClassFilter.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ByteCodeHelper.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ClassDescriptor.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ClassFilter.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/FieldFilter.java branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/BasicInstrumentationTask.java Removed: branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldFilter.java branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldHandled.java branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldHandler.java branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldTransformer.java Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/AbstractClassTransformerImpl.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/BytecodeProvider.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/cglib/BytecodeProviderImpl.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/cglib/CglibClassTransformer.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/BytecodeProviderImpl.java branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/JavassistClassTransformer.java branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/FieldInterceptionHelper.java branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/javassist/FieldInterceptorImpl.java branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/javassist/JavassistHelper.java branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/cglib/InstrumentTask.java branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/InstrumentTask.java Log: ported fix forHHH-1968 to 3.2 branch Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/AbstractClassTransformerImpl.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/AbstractClassTransformerImpl.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/AbstractClassTransformerImpl.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -1,30 +1,23 @@ //$Id: $ package org.hibernate.bytecode; +import org.hibernate.bytecode.util.ClassFilter; +import org.hibernate.bytecode.util.FieldFilter; + import java.security.ProtectionDomain; -import java.util.HashSet; -import java.util.Set; /** * @author Emmanuel Bernard + * @author Steve Ebersole */ public abstract class AbstractClassTransformerImpl implements ClassTransformer { - final private Set entities; - final private String[] packages; + protected final ClassFilter classFilter; + protected final FieldFilter fieldFilter; - - public AbstractClassTransformerImpl(String[] packages, String[] classes) { - this.packages = packages; - if (classes == null) { - this.entities = null; - } - else { - this.entities = new HashSet(); - for ( int i = 0; i < classes.length; i++ ) { - entities.add( classes[i] ); - } - } + protected AbstractClassTransformerImpl(ClassFilter classFilter, FieldFilter fieldFilter) { + this.classFilter = classFilter; + this.fieldFilter = fieldFilter; } public byte[] transform( @@ -33,25 +26,14 @@ Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { - boolean enhance = false; - String safeClassName = className.replace( '/', '.' ); - if ( entities == null && packages == null ) { - enhance = true; + // to be safe... + className = className.replace( '/', '.' ); + if ( classFilter.shouldInstrumentClass( className ) ) { + return doTransform( loader, className, classBeingRedefined, protectionDomain, classfileBuffer ); } - if ( ! enhance && entities != null && entities.contains( safeClassName ) ) { - enhance = true; + else { + return classfileBuffer; } - if ( ! enhance && packages != null ) { - for ( int i = 0; i < packages.length; i++ ) { - if ( safeClassName.startsWith( packages[i] ) ) { - enhance = true; - break; - } - } - } - if ( ! enhance ) return classfileBuffer; - - return doTransform( loader, className, classBeingRedefined, protectionDomain, classfileBuffer ); } protected abstract byte[] doTransform( Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/BytecodeProvider.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/BytecodeProvider.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/BytecodeProvider.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -1,15 +1,16 @@ package org.hibernate.bytecode; +import org.hibernate.bytecode.util.ClassFilter; +import org.hibernate.bytecode.util.FieldFilter; + /** * Contract for providers of bytecode services to Hibernate. * <p/> - * Bytecode requirements break down into basically 4 areas<ol> + * Bytecode requirements break down into basically 3 areas<ol> * <li>proxy generation (both for runtime-lazy-loading and basic proxy generation) * {@link #getProxyFactoryFactory()} * <li>bean relection optimization {@link #getReflectionOptimizer} - * <li>build-time instumentation (not covered by this contract) - * <li>class-load intrumentation {@link #generateDynamicFieldInterceptionClassLoader}; - * (currently only used in the test suite). + * <li>field-access instumentation {@link #getTransformer} * </ol> * * @author Steve Ebersole @@ -36,41 +37,13 @@ public ReflectionOptimizer getReflectionOptimizer(Class clazz, String[] getterNames, String[] setterNames, Class[] types); /** - * Generate a ClassLoader capable of performing dynamic bytecode manipulation - * on classes as they are loaded for the purpose of field-level interception. - * The returned ClassLoader is used for run-time bytecode manipulation as - * opposed to the more common build-time manipulation, since here we get - * into SecurityManager issues and such. - * <p/> - * Currently used only from the Hibernate test suite, although conceivably - * (SecurityManager concerns aside) could be used somehow in running systems. + * Generate a ClassTransformer capable of performing bytecode manipulation. * - * @param parent The parent classloader - * @param classpath The classpath to be searched - * @param packages can be null; use to limit the packages to be loaded - * via this classloader (and transformed). - * @return The appropriate ClassLoader. - */ - public ClassLoader generateDynamicFieldInterceptionClassLoader(ClassLoader parent, String[] classpath, String[] packages); - - /** - * Generate a ClassTransformer capable of performing dynamic bytecode manipulation - * on classes as they are loaded for the purpose of field-level interception. - * The returned ClassTransformer can be combined to an appropriate ClassLoader - * is used for run-time bytecode manipulation as - * opposed to the more common build-time manipulation, since here we get - * into SecurityManager issues and such. - * <p/> - * - * @param packages can be null; use to limit the packages to be transformed - * via this classtransformer. - * @param classes can be null; use to limit the classes to be transformed - * via this class transformer. + * @param classFilter filter used to limit which classes are to be instrumented + * via this ClassTransformer. + * @param fieldFilter filter used to limit which fields are to be instrumented + * via this ClassTransformer. * @return The appropriate ClassTransformer. */ - public ClassTransformer getEntityClassTransformer( - String[] packages, String[] classes - ); - - public void releaseDynamicFieldInterceptionClassLoader(ClassLoader classLoader); + public ClassTransformer getTransformer(ClassFilter classFilter, FieldFilter fieldFilter); } Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/InstrumentedClassLoader.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/InstrumentedClassLoader.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/InstrumentedClassLoader.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,54 @@ +package org.hibernate.bytecode; + +import org.hibernate.bytecode.util.ByteCodeHelper; + +import java.io.InputStream; + +/** + * A specialized classloader which performs bytecode enhancement on class + * definitions as they are loaded into the classloader scope. + * + * @author Emmanuel Bernard + * @author Steve Ebersole + */ +public class InstrumentedClassLoader extends ClassLoader { + + private ClassTransformer classTransformer; + + public InstrumentedClassLoader(ClassLoader parent, ClassTransformer classTransformer) { + super( parent ); + this.classTransformer = classTransformer; + } + + public Class loadClass(String name) throws ClassNotFoundException { + if ( name.startsWith( "java." ) || classTransformer == null ) { + return getParent().loadClass( name ); + } + + Class c = findLoadedClass( name ); + if ( c != null ) { + return c; + } + + InputStream is = this.getResourceAsStream( name.replace( '.', '/' ) + ".class" ); + if ( is == null ) { + throw new ClassNotFoundException( name + " not found" ); + } + + try { + byte[] originalBytecode = ByteCodeHelper.readByteCode( is ); + byte[] transformedBytecode = classTransformer.transform( getParent(), name, null, null, originalBytecode ); + if ( originalBytecode == transformedBytecode ) { + // no transformations took place, so handle it as we would a + // non-instrumented class + return getParent().loadClass( name ); + } + else { + return defineClass( name, transformedBytecode, 0, transformedBytecode.length ); + } + } + catch( Throwable t ) { + throw new ClassNotFoundException( name + " not found", t ); + } + } +} Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/cglib/BytecodeProviderImpl.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/cglib/BytecodeProviderImpl.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/cglib/BytecodeProviderImpl.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -16,6 +16,7 @@ import org.hibernate.bytecode.BytecodeProvider; import org.hibernate.bytecode.ProxyFactoryFactory; import org.hibernate.bytecode.ReflectionOptimizer; +import org.hibernate.bytecode.util.FieldFilter; import org.hibernate.util.StringHelper; import org.objectweb.asm.Type; @@ -84,58 +85,8 @@ } } - public ClassLoader generateDynamicFieldInterceptionClassLoader( - ClassLoader parent, - String[] classpath, - String[] packages) { - return new TransformingClassLoader( - parent, - new ClassLoaderClassFilter( packages ), - new ClassTransformerFactory() { - public ClassTransformer newInstance() { - return new InterceptFieldTransformer( - new InterceptFieldFilter() { - public boolean acceptRead(Type owner, String name) { - return true; - } - public boolean acceptWrite(Type owner, String name) { - return true; - } - } - ); - } - } - ); + public org.hibernate.bytecode.ClassTransformer getTransformer(org.hibernate.bytecode.util.ClassFilter classFilter, FieldFilter fieldFilter) { + return new CglibClassTransformer( classFilter, fieldFilter ); } - public org.hibernate.bytecode.ClassTransformer getEntityClassTransformer( - String[] packages, String[] classes - ) { - return new CglibClassTransformer( packages, classes ); - } - - public void releaseDynamicFieldInterceptionClassLoader(ClassLoader classLoader) { - } - - private static class ClassLoaderClassFilter implements ClassFilter { - private final String[] packages; - - public ClassLoaderClassFilter(String[] packages) { - this.packages = packages; - } - - public boolean accept(String className) { - if ( packages == null ) { - return true; - } - else { - for ( int i = 0; i < packages.length; i++ ) { - if ( className.startsWith( packages[i] ) ) { - return true; - } - } - return false; - } - } - } } Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/cglib/CglibClassTransformer.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/cglib/CglibClassTransformer.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/cglib/CglibClassTransformer.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -2,7 +2,6 @@ package org.hibernate.bytecode.cglib; import java.security.ProtectionDomain; -import java.util.Arrays; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ByteArrayOutputStream; @@ -18,6 +17,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.bytecode.AbstractClassTransformerImpl; +import org.hibernate.bytecode.util.FieldFilter; +import org.hibernate.bytecode.util.ClassFilter; import org.hibernate.HibernateException; import org.objectweb.asm.Attribute; import org.objectweb.asm.Type; @@ -30,13 +31,14 @@ * This interface is then used by Hibernate for some optimizations. * * @author Emmanuel Bernard + * @author Steve Ebersole */ public class CglibClassTransformer extends AbstractClassTransformerImpl { private static Log log = LogFactory.getLog( CglibClassTransformer.class.getName() ); - public CglibClassTransformer(String[] packages, String[] classes) { - super(packages, classes); + public CglibClassTransformer(ClassFilter classFilter, FieldFilter fieldFilter) { + super( classFilter, fieldFilter ); } protected byte[] doTransform( @@ -91,22 +93,29 @@ return false; } - private ClassTransformer getClassTransformer(String[] classInfo) { - if ( Arrays.asList( classInfo ).contains( InterceptFieldEnabled.class.getName() ) ) { + private ClassTransformer getClassTransformer(final String[] classInfo) { + if ( isAlreadyInstrumented( classInfo ) ) { return null; } - else { - return new InterceptFieldTransformer( - new InterceptFieldFilter() { - public boolean acceptRead(Type owner, String name) { - return true; - } + return new InterceptFieldTransformer( + new InterceptFieldFilter() { + public boolean acceptRead(Type owner, String name) { + return fieldFilter.shouldTransformFieldAccess( classInfo[0], owner.getClassName(), name ); + } - public boolean acceptWrite(Type owner, String name) { - return true; - } + public boolean acceptWrite(Type owner, String name) { + return fieldFilter.shouldTransformFieldAccess( classInfo[0], owner.getClassName(), name ); } - ); + } + ); + } + + private boolean isAlreadyInstrumented(String[] classInfo) { + for ( int i = 1; i < classInfo.length; i++ ) { + if ( InterceptFieldEnabled.class.getName().equals( classInfo[i] ) ) { + return true; + } } + return false; } } Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/BytecodeProviderImpl.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/BytecodeProviderImpl.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/BytecodeProviderImpl.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -8,6 +8,8 @@ import org.hibernate.bytecode.ClassTransformer; import org.hibernate.bytecode.ProxyFactoryFactory; import org.hibernate.bytecode.ReflectionOptimizer; +import org.hibernate.bytecode.util.ClassFilter; +import org.hibernate.bytecode.util.FieldFilter; import org.hibernate.util.StringHelper; /** @@ -75,20 +77,8 @@ } } - public ClassLoader generateDynamicFieldInterceptionClassLoader(ClassLoader parent, String[] classpath, String[] packages) { - return new TransformingClassLoader( parent, classpath ); + public ClassTransformer getTransformer(ClassFilter classFilter, FieldFilter fieldFilter) { + return new JavassistClassTransformer( classFilter, fieldFilter ); } - public ClassTransformer getEntityClassTransformer( - String[] packages, String[] classes - ) { - return new JavassistClassTransformer( packages, classes ); - } - - public void releaseDynamicFieldInterceptionClassLoader(ClassLoader classLoader) { - if ( ! TransformingClassLoader.class.isAssignableFrom( classLoader.getClass() ) ) { - return; - } - ( ( TransformingClassLoader ) classLoader ).release(); - } } Copied: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldFilter.java (from rev 10206, branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldFilter.java) =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldFilter.java 2006-08-03 19:59:42 UTC (rev 10206) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldFilter.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,28 @@ +package org.hibernate.bytecode.javassist; + +/** + * Contract for deciding whether fields should be read and/or write intercepted. + * + * @author Muga Nishizawa + */ +public interface FieldFilter { + /** + * Should the given field be read intercepted? + * + * @param desc + * @param name + * @return true if the given field should be read intercepted; otherwise + * false. + */ + boolean handleRead(String desc, String name); + + /** + * Should the given field be write intercepted? + * + * @param desc + * @param name + * @return true if the given field should be write intercepted; otherwise + * false. + */ + boolean handleWrite(String desc, String name); +} Property changes on: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldFilter.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:eol-style + native Copied: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldHandled.java (from rev 10206, branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldHandled.java) =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldHandled.java 2006-08-03 19:59:42 UTC (rev 10206) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldHandled.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,23 @@ +package org.hibernate.bytecode.javassist; + +/** + * Interface introduced to the enhanced class in order to be able to + * inject a {@link FieldHandler} to define the interception behavior. + * + * @author Muga Nishizawa + */ +public interface FieldHandled { + /** + * Inject the field interception handler to be used. + * + * @param handler The field interception handler. + */ + public void setFieldHandler(FieldHandler handler); + + /** + * Access to the current field interception handler. + * + * @return The current field interception handler. + */ + public FieldHandler getFieldHandler(); +} Property changes on: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldHandled.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:eol-style + native Copied: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldHandler.java (from rev 10206, branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldHandler.java) =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldHandler.java 2006-08-03 19:59:42 UTC (rev 10206) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldHandler.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,56 @@ +package org.hibernate.bytecode.javassist; + +/** + * The interface defining how interception of a field should be handled. + * + * @author Muga Nishizawa + */ +public interface FieldHandler { + + /** + * Called to handle writing an int value to a given field. + * + * @param obj ? + * @param name The name of the field being written + * @param oldValue The old field value + * @param newValue The new field value. + * @return ? + */ + int writeInt(Object obj, String name, int oldValue, int newValue); + + char writeChar(Object obj, String name, char oldValue, char newValue); + + byte writeByte(Object obj, String name, byte oldValue, byte newValue); + + boolean writeBoolean(Object obj, String name, boolean oldValue, + boolean newValue); + + short writeShort(Object obj, String name, short oldValue, short newValue); + + float writeFloat(Object obj, String name, float oldValue, float newValue); + + double writeDouble(Object obj, String name, double oldValue, double newValue); + + long writeLong(Object obj, String name, long oldValue, long newValue); + + Object writeObject(Object obj, String name, Object oldValue, Object newValue); + + int readInt(Object obj, String name, int oldValue); + + char readChar(Object obj, String name, char oldValue); + + byte readByte(Object obj, String name, byte oldValue); + + boolean readBoolean(Object obj, String name, boolean oldValue); + + short readShort(Object obj, String name, short oldValue); + + float readFloat(Object obj, String name, float oldValue); + + double readDouble(Object obj, String name, double oldValue); + + long readLong(Object obj, String name, long oldValue); + + Object readObject(Object obj, String name, Object oldValue); + +} Property changes on: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldHandler.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:eol-style + native Copied: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldTransformer.java (from rev 10206, branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldTransformer.java) =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/javassist/FieldTransformer.java 2006-08-03 19:59:42 UTC (rev 10206) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldTransformer.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,592 @@ +package org.hibernate.bytecode.javassist; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import javassist.CannotCompileException; +import javassist.bytecode.AccessFlag; +import javassist.bytecode.BadBytecode; +import javassist.bytecode.Bytecode; +import javassist.bytecode.ClassFile; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.CodeIterator; +import javassist.bytecode.ConstPool; +import javassist.bytecode.Descriptor; +import javassist.bytecode.FieldInfo; +import javassist.bytecode.MethodInfo; +import javassist.bytecode.Opcode; +import org.hibernate.bytecode.javassist.FieldFilter; +import org.hibernate.bytecode.javassist.FieldHandled; +import org.hibernate.bytecode.javassist.FieldHandler; + +/** + * The thing that handles actual class enhancement in regards to + * intercepting field accesses. + * + * @author Muga Nishizawa + */ +public class FieldTransformer { + + private static final String EACH_READ_METHOD_PREFIX = "$javassist_read_"; + + private static final String EACH_WRITE_METHOD_PREFIX = "$javassist_write_"; + + private static final String FIELD_HANDLED_TYPE_NAME = FieldHandled.class + .getName(); + + private static final String HANDLER_FIELD_NAME = "$JAVASSIST_READ_WRITE_HANDLER"; + + private static final String FIELD_HANDLER_TYPE_NAME = FieldHandler.class + .getName(); + + private static final String HANDLER_FIELD_DESCRIPTOR = 'L' + FIELD_HANDLER_TYPE_NAME + .replace('.', '/') + ';'; + + private static final String GETFIELDHANDLER_METHOD_NAME = "getFieldHandler"; + + private static final String SETFIELDHANDLER_METHOD_NAME = "setFieldHandler"; + + private static final String GETFIELDHANDLER_METHOD_DESCRIPTOR = "()" + + HANDLER_FIELD_DESCRIPTOR; + + private static final String SETFIELDHANDLER_METHOD_DESCRIPTOR = "(" + + HANDLER_FIELD_DESCRIPTOR + ")V"; + + private FieldFilter filter; + + private HashMap readableFields; + + private HashMap writableFields; + + public FieldTransformer() { + this(null); + } + + public FieldTransformer(FieldFilter f) { + filter = f; + readableFields = new HashMap(); + writableFields = new HashMap(); + } + + public void setFieldFilter(FieldFilter f) { + filter = f; + } + + public void transform(File file) throws Exception { + DataInputStream in = new DataInputStream(new FileInputStream(file)); + ClassFile classfile = new ClassFile(in); + transform(classfile); + DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); + try { + classfile.write(out); + } finally { + out.close(); + } + } + + public void transform(ClassFile classfile) throws Exception { + if (classfile.isInterface()) { + return; + } + try { + addFieldHandlerField(classfile); + addGetFieldHandlerMethod(classfile); + addSetFieldHandlerMethod(classfile); + addFieldHandledInterface(classfile); + addReadWriteMethods(classfile); + transformInvokevirtualsIntoPutAndGetfields(classfile); + } catch (CannotCompileException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + private void addFieldHandlerField(ClassFile classfile) + throws CannotCompileException { + ConstPool cp = classfile.getConstPool(); + FieldInfo finfo = new FieldInfo(cp, HANDLER_FIELD_NAME, + HANDLER_FIELD_DESCRIPTOR); + finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.TRANSIENT); + classfile.addField(finfo); + } + + private void addGetFieldHandlerMethod(ClassFile classfile) + throws CannotCompileException { + ConstPool cp = classfile.getConstPool(); + int this_class_index = cp.getThisClassInfo(); + MethodInfo minfo = new MethodInfo(cp, GETFIELDHANDLER_METHOD_NAME, + GETFIELDHANDLER_METHOD_DESCRIPTOR); + /* local variable | this | */ + Bytecode code = new Bytecode(cp, 2, 1); + // aload_0 // load this + code.addAload(0); + // getfield // get field "$JAVASSIST_CALLBACK" defined already + code.addOpcode(Opcode.GETFIELD); + int field_index = cp.addFieldrefInfo(this_class_index, + HANDLER_FIELD_NAME, HANDLER_FIELD_DESCRIPTOR); + code.addIndex(field_index); + // areturn // return the value of the field + code.addOpcode(Opcode.ARETURN); + minfo.setCodeAttribute(code.toCodeAttribute()); + minfo.setAccessFlags(AccessFlag.PUBLIC); + classfile.addMethod(minfo); + } + + private void addSetFieldHandlerMethod(ClassFile classfile) + throws CannotCompileException { + ConstPool cp = classfile.getConstPool(); + int this_class_index = cp.getThisClassInfo(); + MethodInfo minfo = new MethodInfo(cp, SETFIELDHANDLER_METHOD_NAME, + SETFIELDHANDLER_METHOD_DESCRIPTOR); + /* local variables | this | callback | */ + Bytecode code = new Bytecode(cp, 3, 3); + // aload_0 // load this + code.addAload(0); + // aload_1 // load callback + code.addAload(1); + // putfield // put field "$JAVASSIST_CALLBACK" defined already + code.addOpcode(Opcode.PUTFIELD); + int field_index = cp.addFieldrefInfo(this_class_index, + HANDLER_FIELD_NAME, HANDLER_FIELD_DESCRIPTOR); + code.addIndex(field_index); + // return + code.addOpcode(Opcode.RETURN); + minfo.setCodeAttribute(code.toCodeAttribute()); + minfo.setAccessFlags(AccessFlag.PUBLIC); + classfile.addMethod(minfo); + } + + private void addFieldHandledInterface(ClassFile classfile) { + String[] interfaceNames = classfile.getInterfaces(); + String[] newInterfaceNames = new String[interfaceNames.length + 1]; + System.arraycopy(interfaceNames, 0, newInterfaceNames, 0, + interfaceNames.length); + newInterfaceNames[newInterfaceNames.length - 1] = FIELD_HANDLED_TYPE_NAME; + classfile.setInterfaces(newInterfaceNames); + } + + private void addReadWriteMethods(ClassFile classfile) + throws CannotCompileException { + List fields = classfile.getFields(); + for (Iterator field_iter = fields.iterator(); field_iter.hasNext();) { + FieldInfo finfo = (FieldInfo) field_iter.next(); + if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0 + && (!finfo.getName().equals(HANDLER_FIELD_NAME))) { + // case of non-static field + if (filter.handleRead(finfo.getDescriptor(), finfo + .getName())) { + addReadMethod(classfile, finfo); + readableFields.put(finfo.getName(), finfo + .getDescriptor()); + } + if (filter.handleWrite(finfo.getDescriptor(), finfo + .getName())) { + addWriteMethod(classfile, finfo); + writableFields.put(finfo.getName(), finfo + .getDescriptor()); + } + } + } + } + + private void addReadMethod(ClassFile classfile, FieldInfo finfo) + throws CannotCompileException { + ConstPool cp = classfile.getConstPool(); + int this_class_index = cp.getThisClassInfo(); + String desc = "()" + finfo.getDescriptor(); + MethodInfo minfo = new MethodInfo(cp, EACH_READ_METHOD_PREFIX + + finfo.getName(), desc); + /* local variables | target obj | each oldvalue | */ + Bytecode code = new Bytecode(cp, 5, 3); + // aload_0 + code.addAload(0); + // getfield // get each field + code.addOpcode(Opcode.GETFIELD); + int base_field_index = cp.addFieldrefInfo(this_class_index, finfo + .getName(), finfo.getDescriptor()); + code.addIndex(base_field_index); + // aload_0 + code.addAload(0); + // invokeinterface // invoke Enabled.getInterceptFieldCallback() + int enabled_class_index = cp.addClassInfo(FIELD_HANDLED_TYPE_NAME); + code.addInvokeinterface(enabled_class_index, + GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR, + 1); + // ifnonnull + code.addOpcode(Opcode.IFNONNULL); + code.addIndex(4); + // *return // each type + addTypeDependDataReturn(code, finfo.getDescriptor()); + // *store_1 // each type + addTypeDependDataStore(code, finfo.getDescriptor(), 1); + // aload_0 + code.addAload(0); + // invokeinterface // invoke Enabled.getInterceptFieldCallback() + code.addInvokeinterface(enabled_class_index, + GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR, + 1); + // aload_0 + code.addAload(0); + // ldc // name of the field + code.addLdc(finfo.getName()); + // *load_1 // each type + addTypeDependDataLoad(code, finfo.getDescriptor(), 1); + // invokeinterface // invoke Callback.read*() // each type + addInvokeFieldHandlerMethod(classfile, code, finfo.getDescriptor(), + true); + // *return // each type + addTypeDependDataReturn(code, finfo.getDescriptor()); + + minfo.setCodeAttribute(code.toCodeAttribute()); + minfo.setAccessFlags(AccessFlag.PUBLIC); + classfile.addMethod(minfo); + } + + private void addWriteMethod(ClassFile classfile, FieldInfo finfo) + throws CannotCompileException { + ConstPool cp = classfile.getConstPool(); + int this_class_index = cp.getThisClassInfo(); + String desc = "(" + finfo.getDescriptor() + ")V"; + MethodInfo minfo = new MethodInfo(cp, EACH_WRITE_METHOD_PREFIX + + finfo.getName(), desc); + /* local variables | target obj | each oldvalue | */ + Bytecode code = new Bytecode(cp, 6, 3); + // aload_0 + code.addAload(0); + // invokeinterface // enabled.getInterceptFieldCallback() + int enabled_class_index = cp.addClassInfo(FIELD_HANDLED_TYPE_NAME); + code.addInvokeinterface(enabled_class_index, + GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR, + 1); + // ifnonnull (label1) + code.addOpcode(Opcode.IFNONNULL); + code.addIndex(9); + // aload_0 + code.addAload(0); + // *load_1 + addTypeDependDataLoad(code, finfo.getDescriptor(), 1); + // putfield + code.addOpcode(Opcode.PUTFIELD); + int base_field_index = cp.addFieldrefInfo(this_class_index, finfo + .getName(), finfo.getDescriptor()); + code.addIndex(base_field_index); + code.growStack(-Descriptor.dataSize(finfo.getDescriptor())); + // return ; + code.addOpcode(Opcode.RETURN); + // aload_0 + code.addAload(0); + // dup + code.addOpcode(Opcode.DUP); + // invokeinterface // enabled.getInterceptFieldCallback() + code.addInvokeinterface(enabled_class_index, + GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR, + 1); + // aload_0 + code.addAload(0); + // ldc // field name + code.addLdc(finfo.getName()); + // aload_0 + code.addAload(0); + // getfield // old value of the field + code.addOpcode(Opcode.GETFIELD); + code.addIndex(base_field_index); + code.growStack(Descriptor.dataSize(finfo.getDescriptor()) - 1); + // *load_1 + addTypeDependDataLoad(code, finfo.getDescriptor(), 1); + // invokeinterface // callback.write*(..) + addInvokeFieldHandlerMethod(classfile, code, finfo.getDescriptor(), + false); + // putfield // new value of the field + code.addOpcode(Opcode.PUTFIELD); + code.addIndex(base_field_index); + code.growStack(-Descriptor.dataSize(finfo.getDescriptor())); + // return + code.addOpcode(Opcode.RETURN); + + minfo.setCodeAttribute(code.toCodeAttribute()); + minfo.setAccessFlags(AccessFlag.PUBLIC); + classfile.addMethod(minfo); + } + + private void transformInvokevirtualsIntoPutAndGetfields(ClassFile classfile) + throws CannotCompileException { + List methods = classfile.getMethods(); + for (Iterator method_iter = methods.iterator(); method_iter.hasNext();) { + MethodInfo minfo = (MethodInfo) method_iter.next(); + String methodName = minfo.getName(); + if (methodName.startsWith(EACH_READ_METHOD_PREFIX) + || methodName.startsWith(EACH_WRITE_METHOD_PREFIX) + || methodName.equals(GETFIELDHANDLER_METHOD_NAME) + || methodName.equals(SETFIELDHANDLER_METHOD_NAME)) { + continue; + } + CodeAttribute codeAttr = minfo.getCodeAttribute(); + if (codeAttr == null) { + return; + } + CodeIterator iter = codeAttr.iterator(); + while (iter.hasNext()) { + try { + int pos = iter.next(); + pos = transformInvokevirtualsIntoGetfields(classfile, iter, + pos); + pos = transformInvokevirtualsIntoPutfields(classfile, iter, + pos); + + } catch (BadBytecode e) { + throw new CannotCompileException(e); + } + } + } + } + + private int transformInvokevirtualsIntoGetfields(ClassFile classfile, + CodeIterator iter, int pos) { + ConstPool cp = classfile.getConstPool(); + int c = iter.byteAt(pos); + if (c != Opcode.GETFIELD) { + return pos; + } + int index = iter.u16bitAt(pos + 1); + String fieldName = cp.getFieldrefName(index); + String className = cp.getFieldrefClassName(index); + if ((!classfile.getName().equals(className)) + || (!readableFields.containsKey(fieldName))) { + return pos; + } + String desc = "()" + (String) readableFields.get(fieldName); + int read_method_index = cp.addMethodrefInfo(cp.getThisClassInfo(), + EACH_READ_METHOD_PREFIX + fieldName, desc); + iter.writeByte(Opcode.INVOKEVIRTUAL, pos); + iter.write16bit(read_method_index, pos + 1); + return pos; + } + + private int transformInvokevirtualsIntoPutfields(ClassFile classfile, + CodeIterator iter, int pos) { + ConstPool cp = classfile.getConstPool(); + int c = iter.byteAt(pos); + if (c != Opcode.PUTFIELD) { + return pos; + } + int index = iter.u16bitAt(pos + 1); + String fieldName = cp.getFieldrefName(index); + String className = cp.getFieldrefClassName(index); + if ((!classfile.getName().equals(className)) + || (!writableFields.containsKey(fieldName))) { + return pos; + } + String desc = "(" + (String) writableFields.get(fieldName) + ")V"; + int write_method_index = cp.addMethodrefInfo(cp.getThisClassInfo(), + EACH_WRITE_METHOD_PREFIX + fieldName, desc); + iter.writeByte(Opcode.INVOKEVIRTUAL, pos); + iter.write16bit(write_method_index, pos + 1); + return pos; + } + + private static void addInvokeFieldHandlerMethod(ClassFile classfile, + Bytecode code, String typeName, boolean isReadMethod) { + ConstPool cp = classfile.getConstPool(); + // invokeinterface + int callback_type_index = cp.addClassInfo(FIELD_HANDLER_TYPE_NAME); + if ((typeName.charAt(0) == 'L') + && (typeName.charAt(typeName.length() - 1) == ';') + || (typeName.charAt(0) == '[')) { + // reference type + int indexOfL = typeName.indexOf('L'); + String type; + if (indexOfL == 0) { + // not array + type = typeName.substring(1, typeName.length() - 1); + type = type.replace('/', '.'); + } else if (indexOfL == -1) { + // array of primitive type + // do nothing + type = typeName; + } else { + // array of reference type + type = typeName.replace('/', '.'); + } + if (isReadMethod) { + code + .addInvokeinterface( + callback_type_index, + "readObject", + "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", + 4); + // checkcast + code.addCheckcast(type); + } else { + code + .addInvokeinterface( + callback_type_index, + "writeObject", + "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + 5); + // checkcast + code.addCheckcast(type); + } + } else if (typeName.equals("Z")) { + // boolean + if (isReadMethod) { + code.addInvokeinterface(callback_type_index, "readBoolean", + "(Ljava/lang/Object;Ljava/lang/String;Z)Z", 4); + } else { + code.addInvokeinterface(callback_type_index, "writeBoolean", + "(Ljava/lang/Object;Ljava/lang/String;ZZ)Z", 5); + } + } else if (typeName.equals("B")) { + // byte + if (isReadMethod) { + code.addInvokeinterface(callback_type_index, "readByte", + "(Ljava/lang/Object;Ljava/lang/String;B)B", 4); + } else { + code.addInvokeinterface(callback_type_index, "writeByte", + "(Ljava/lang/Object;Ljava/lang/String;BB)B", 5); + } + } else if (typeName.equals("C")) { + // char + if (isReadMethod) { + code.addInvokeinterface(callback_type_index, "readChar", + "(Ljava/lang/Object;Ljava/lang/String;C)C", 4); + } else { + code.addInvokeinterface(callback_type_index, "writeChar", + "(Ljava/lang/Object;Ljava/lang/String;CC)C", 5); + } + } else if (typeName.equals("I")) { + // int + if (isReadMethod) { + code.addInvokeinterface(callback_type_index, "readInt", + "(Ljava/lang/Object;Ljava/lang/String;I)I", 4); + } else { + code.addInvokeinterface(callback_type_index, "writeInt", + "(Ljava/lang/Object;Ljava/lang/String;II)I", 5); + } + } else if (typeName.equals("S")) { + // short + if (isReadMethod) { + code.addInvokeinterface(callback_type_index, "readShort", + "(Ljava/lang/Object;Ljava/lang/String;S)S", 4); + } else { + code.addInvokeinterface(callback_type_index, "writeShort", + "(Ljava/lang/Object;Ljava/lang/String;SS)S", 5); + } + } else if (typeName.equals("D")) { + // double + if (isReadMethod) { + code.addInvokeinterface(callback_type_index, "readDouble", + "(Ljava/lang/Object;Ljava/lang/String;D)D", 5); + } else { + code.addInvokeinterface(callback_type_index, "writeDouble", + "(Ljava/lang/Object;Ljava/lang/String;DD)D", 7); + } + } else if (typeName.equals("F")) { + // float + if (isReadMethod) { + code.addInvokeinterface(callback_type_index, "readFloat", + "(Ljava/lang/Object;Ljava/lang/String;F)F", 4); + } else { + code.addInvokeinterface(callback_type_index, "writeFloat", + "(Ljava/lang/Object;Ljava/lang/String;FF)F", 5); + } + } else if (typeName.equals("J")) { + // long + if (isReadMethod) { + code.addInvokeinterface(callback_type_index, "readLong", + "(Ljava/lang/Object;Ljava/lang/String;J)J", 5); + } else { + code.addInvokeinterface(callback_type_index, "writeLong", + "(Ljava/lang/Object;Ljava/lang/String;JJ)J", 7); + } + } else { + // bad type + throw new RuntimeException("bad type: " + typeName); + } + } + + private static void addTypeDependDataLoad(Bytecode code, String typeName, + int i) { + if ((typeName.charAt(0) == 'L') + && (typeName.charAt(typeName.length() - 1) == ';') + || (typeName.charAt(0) == '[')) { + // reference type + code.addAload(i); + } else if (typeName.equals("Z") || typeName.equals("B") + || typeName.equals("C") || typeName.equals("I") + || typeName.equals("S")) { + // boolean, byte, char, int, short + code.addIload(i); + } else if (typeName.equals("D")) { + // double + code.addDload(i); + } else if (typeName.equals("F")) { + // float + code.addFload(i); + } else if (typeName.equals("J")) { + // long + code.addLload(i); + } else { + // bad type + throw new RuntimeException("bad type: " + typeName); + } + } + + private static void addTypeDependDataStore(Bytecode code, String typeName, + int i) { + if ((typeName.charAt(0) == 'L') + && (typeName.charAt(typeName.length() - 1) == ';') + || (typeName.charAt(0) == '[')) { + // reference type + code.addAstore(i); + } else if (typeName.equals("Z") || typeName.equals("B") + || typeName.equals("C") || typeName.equals("I") + || typeName.equals("S")) { + // boolean, byte, char, int, short + code.addIstore(i); + } else if (typeName.equals("D")) { + // double + code.addDstore(i); + } else if (typeName.equals("F")) { + // float + code.addFstore(i); + } else if (typeName.equals("J")) { + // long + code.addLstore(i); + } else { + // bad type + throw new RuntimeException("bad type: " + typeName); + } + } + + private static void addTypeDependDataReturn(Bytecode code, String typeName) { + if ((typeName.charAt(0) == 'L') + && (typeName.charAt(typeName.length() - 1) == ';') + || (typeName.charAt(0) == '[')) { + // reference type + code.addOpcode(Opcode.ARETURN); + } else if (typeName.equals("Z") || typeName.equals("B") + || typeName.equals("C") || typeName.equals("I") + || typeName.equals("S")) { + // boolean, byte, char, int, short + code.addOpcode(Opcode.IRETURN); + } else if (typeName.equals("D")) { + // double + code.addOpcode(Opcode.DRETURN); + } else if (typeName.equals("F")) { + // float + code.addOpcode(Opcode.FRETURN); + } else if (typeName.equals("J")) { + // long + code.addOpcode(Opcode.LRETURN); + } else { + // bad type + throw new RuntimeException("bad type: " + typeName); + } + } + +} Property changes on: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/FieldTransformer.java ___________________________________________________________________ Name: svn:keywords + Author Date Id Revision Name: svn:eol-style + native Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/JavassistClassTransformer.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/JavassistClassTransformer.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/javassist/JavassistClassTransformer.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -13,22 +13,21 @@ import org.apache.commons.logging.LogFactory; import org.hibernate.HibernateException; import org.hibernate.bytecode.AbstractClassTransformerImpl; -import org.hibernate.tool.instrument.javassist.FieldFilter; -import org.hibernate.tool.instrument.javassist.FieldHandled; -import org.hibernate.tool.instrument.javassist.FieldTransformer; +import org.hibernate.bytecode.util.ClassFilter; /** * Enhance the classes allowing them to implements InterceptFieldEnabled * This interface is then used by Hibernate for some optimizations. * * @author Emmanuel Bernard + * @author Steve Ebersole */ public class JavassistClassTransformer extends AbstractClassTransformerImpl { private static Log log = LogFactory.getLog( JavassistClassTransformer.class.getName() ); - public JavassistClassTransformer(String[] packages, String[] classes) { - super(packages, classes); + public JavassistClassTransformer(ClassFilter classFilter, org.hibernate.bytecode.util.FieldFilter fieldFilter) { + super( classFilter, fieldFilter ); } protected byte[] doTransform( @@ -75,23 +74,29 @@ return classfileBuffer; } - protected FieldTransformer getFieldTransformer(ClassFile classfile) { + protected FieldTransformer getFieldTransformer(final ClassFile classfile) { if ( alreadyInstrumented( classfile ) ) { return null; } - else { - return new FieldTransformer( - new FieldFilter() { - public boolean handleRead(String desc, String name) { - return true; - } + return new FieldTransformer( + new FieldFilter() { + public boolean handleRead(String desc, String name) { + return fieldFilter.shouldInstrumentField( classfile.getName(), name ); + } - public boolean handleWrite(String desc, String name) { - return true; - } + public boolean handleWrite(String desc, String name) { + return fieldFilter.shouldInstrumentField( classfile.getName(), name ); } - ); - } + + public boolean handleReadAccess(String fieldOwnerClassName, String fieldName) { + return fieldFilter.shouldTransformFieldAccess( classfile.getName(), fieldOwnerClassName, fieldName ); + } + + public boolean handleWriteAccess(String fieldOwnerClassName, String fieldName) { + return fieldFilter.shouldTransformFieldAccess( classfile.getName(), fieldOwnerClassName, fieldName ); + } + } + ); } private boolean alreadyInstrumented(ClassFile classfile) { Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/BasicClassFilter.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/BasicClassFilter.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/BasicClassFilter.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,59 @@ +package org.hibernate.bytecode.util; + +import java.util.Set; +import java.util.HashSet; + +/** + * BasicClassFilter provides class filtering based on a series of packages to + * be included and/or a series of explicit class names to be included. If + * neither is specified, then no restrictions are applied. + * + * @author Steve Ebersole + */ +public class BasicClassFilter implements ClassFilter { + private final String[] includedPackages; + private final Set includedClassNames = new HashSet(); + private final boolean isAllEmpty; + + public BasicClassFilter() { + this( null, null ); + } + + public BasicClassFilter(String[] includedPackages, String[] includedClassNames) { + this.includedPackages = includedPackages; + if ( includedClassNames != null ) { + for ( int i = 0; i < includedClassNames.length; i++ ) { + this.includedClassNames.add( includedClassNames[i] ); + } + } + + isAllEmpty = ( this.includedPackages == null || this.includedPackages.length == 0 ) + && ( this.includedClassNames.isEmpty() ); + } + + public boolean shouldInstrumentClass(String className) { + if ( isAllEmpty ) { + return true; + } + else if ( includedClassNames.contains( className ) ) { + return true; + } + else if ( isInIncludedPackage( className ) ) { + return true; + } + else { + return false; + } + } + + private boolean isInIncludedPackage(String className) { + if ( includedPackages != null ) { + for ( int i = 0; i < includedPackages.length; i++ ) { + if ( className.startsWith( includedPackages[i] ) ) { + return true; + } + } + } + return false; + } +} Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ByteCodeHelper.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ByteCodeHelper.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ByteCodeHelper.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,78 @@ +package org.hibernate.bytecode.util; + +import java.io.InputStream; +import java.io.IOException; +import java.io.File; +import java.io.FileInputStream; +import java.io.ByteArrayOutputStream; +import java.io.BufferedInputStream; +import java.util.zip.ZipInputStream; + +/** + * A helper for reading byte code from various input sources. + * + * @author Steve Ebersole + */ +public class ByteCodeHelper { + private ByteCodeHelper() { + } + + /** + * Reads class byte array info from the given input stream. + * <p/> + * The stream is closed within this method! + * + * @param inputStream + * @return + * @throws IOException + */ + public static byte[] readByteCode(InputStream inputStream) throws IOException { + if ( inputStream == null ) { + throw new IOException( "null input stream" ); + } + + byte[] buffer = new byte[409600]; + byte[] classBytes = new byte[0]; + int r = 0; + + try { + r = inputStream.read( buffer ); + while ( r >= buffer.length ) { + byte[] temp = new byte[ classBytes.length + buffer.length ]; + System.arraycopy( classBytes, 0, temp, 0, classBytes.length ); + System.arraycopy( buffer, 0, temp, classBytes.length, buffer.length ); + classBytes = temp; + } + if ( r != -1 ) { + byte[] temp = new byte[ classBytes.length + r ]; + System.arraycopy( classBytes, 0, temp, 0, classBytes.length ); + System.arraycopy( buffer, 0, temp, classBytes.length, r ); + classBytes = temp; + } + } + finally { + try { + inputStream.close(); + } + catch (IOException ignore) { + // intentionally empty + } + } + + return classBytes; + } + + public static byte[] readByteCode(File file) throws IOException { + return ByteCodeHelper.readByteCode( new FileInputStream( file ) ); + } + + public static byte[] readByteCode(ZipInputStream zip) throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + InputStream in = new BufferedInputStream( zip ); + int b; + while ( ( b = in.read() ) != -1 ) { + bout.write( b ); + } + return bout.toByteArray(); + } +} Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ClassDescriptor.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ClassDescriptor.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ClassDescriptor.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,30 @@ +package org.hibernate.bytecode.util; + +/** + * Contract describing the information Hibernate needs in terms of instrumenting + * a class, either via ant task or dynamic classloader. + * + * @author Steve Ebersole + */ +public interface ClassDescriptor { + /** + * The name of the class. + * + * @return The class name. + */ + public String getName(); + + /** + * Determine if the class is already instrumented. + * + * @return True if already instrumented; false otherwise. + */ + public boolean isInstrumented(); + + /** + * The bytes making up the class' bytecode. + * + * @return The bytecode bytes. + */ + public byte[] getBytes(); +} Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ClassFilter.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ClassFilter.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/ClassFilter.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,10 @@ +package org.hibernate.bytecode.util; + +/** + * Used to determine whether a class should be instrumented. + * + * @author Steve Ebersole + */ +public interface ClassFilter { + public boolean shouldInstrumentClass(String className); +} Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/FieldFilter.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/FieldFilter.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/bytecode/util/FieldFilter.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,29 @@ +package org.hibernate.bytecode.util; + +/** + * Used to determine whether a field reference should be instrumented. + * + * @author Steve Ebersole + */ +public interface FieldFilter { + /** + * Should this field definition be instrumented? + * + * @param className The name of the class currently being processed + * @param fieldName The name of the field being checked. + * @return True if we should instrument this field. + */ + public boolean shouldInstrumentField(String className, String fieldName); + + /** + * Should we instrument *access to* the given field. This differs from + * {@link #shouldInstrumentField} in that here we are talking about a particular usage of + * a field. + * + * @param transformingClassName The class currently being transformed. + * @param fieldOwnerClassName The name of the class owning this field being checked. + * @param fieldName The name of the field being checked. + * @return True if this access should be transformed. + */ + public boolean shouldTransformFieldAccess(String transformingClassName, String fieldOwnerClassName, String fieldName); +} Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/FieldInterceptionHelper.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/FieldInterceptionHelper.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/FieldInterceptionHelper.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -27,7 +27,7 @@ Class[] definedInterfaces = entityClass.getInterfaces(); for ( int i = 0; i < definedInterfaces.length; i++ ) { if ( "net.sf.cglib.transform.impl.InterceptFieldEnabled".equals( definedInterfaces[i].getName() ) - || "org.hibernate.tool.instrument.javassist.FieldHandled".equals( definedInterfaces[i].getName() ) ) { + || "org.hibernate.bytecode.javassist.FieldHandled".equals( definedInterfaces[i].getName() ) ) { return true; } } @@ -48,7 +48,7 @@ // we have a CGLIB enhanced entity return CGLIBHelper.extractFieldInterceptor( entity ); } - else if ( "org.hibernate.tool.instrument.javassist.FieldHandled".equals( definedInterfaces[i].getName() ) ) { + else if ( "org.hibernate.bytecode.javassist.FieldHandled".equals( definedInterfaces[i].getName() ) ) { // we have a Javassist enhanced entity return JavassistHelper.extractFieldInterceptor( entity ); } @@ -68,7 +68,7 @@ // we have a CGLIB enhanced entity return CGLIBHelper.injectFieldInterceptor( entity, entityName, uninitializedFieldNames, session ); } - else if ( "org.hibernate.tool.instrument.javassist.FieldHandled".equals( definedInterfaces[i].getName() ) ) { + else if ( "org.hibernate.bytecode.javassist.FieldHandled".equals( definedInterfaces[i].getName() ) ) { // we have a Javassist enhanced entity return JavassistHelper.injectFieldInterceptor( entity, entityName, uninitializedFieldNames, session ); } Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/javassist/FieldInterceptorImpl.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/javassist/FieldInterceptorImpl.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/javassist/FieldInterceptorImpl.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -4,7 +4,7 @@ import java.io.Serializable; import java.util.Set; -import org.hibernate.tool.instrument.javassist.FieldHandler; +import org.hibernate.bytecode.javassist.FieldHandler; import org.hibernate.intercept.AbstractFieldInterceptor; import org.hibernate.engine.SessionImplementor; import org.hibernate.proxy.HibernateProxy; Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/javassist/JavassistHelper.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/javassist/JavassistHelper.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/intercept/javassist/JavassistHelper.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -2,7 +2,7 @@ import org.hibernate.intercept.FieldInterceptor; import org.hibernate.engine.SessionImplementor; -import org.hibernate.tool.instrument.javassist.FieldHandled; +import org.hibernate.bytecode.javassist.FieldHandled; import java.util.Set; Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/BasicInstrumentationTask.java =================================================================== --- branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/BasicInstrumentationTask.java 2006-08-03 20:16:18 UTC (rev 10208) +++ branches/Branch_3_2/Hibernate3/src/org/hibernate/tool/instrument/BasicInstrumentationTask.java 2006-08-03 20:35:26 UTC (rev 10209) @@ -0,0 +1,383 @@ +package org.hibernate.tool.instrument; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.types.FileSet; +import org.hibernate.bytecode.util.ClassDescriptor; +import org.hibernate.bytecode.util.ByteCodeHelper; +import org.hibernate.bytecode.util.FieldFilter; +import org.hibernate.bytecode.ClassTransformer; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; +import java.util.HashSet; +import java.util.zip.ZipInputStream; +im... [truncated message content] |