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] |