org.bsf.smartValueObject.tools
Class JavaAssistInstrumentor

java.lang.Object
  |
  +--org.bsf.smartValueObject.tools.JavaAssistInstrumentor
All Implemented Interfaces:
Instrumentor

public class JavaAssistInstrumentor
extends java.lang.Object
implements Instrumentor

Javassist specific implementation.

This class makes heavy use of advanced javassist features like runtime compilation.

See Also:
Instrumentor, Javassist homepage

Nested Class Summary
private static class JavaAssistInstrumentor.InstClassLoader
          Custom ClassLoader using CtClass.
 
Nested classes inherited from class org.bsf.smartValueObject.tools.Instrumentor
Instrumentor.SmartReplacements
 
Field Summary
private static javassist.CodeConverter converter
          The codeconverter to be used to change field access.
private  javassist.CtClass ctclass
          A The modified class in javassist's representation.
private static JavaAssistInstrumentor.InstClassLoader instCL
          Custom classloader to define classes at runtime.
private static org.apache.commons.logging.Log log
           
private static javassist.ClassPool pool
          Default pool to obtain CtClasses from.
 
Fields inherited from interface org.bsf.smartValueObject.tools.Instrumentor
CLEANMETHOD, CREATEDMETHOD, CREATEMETHOD, DELETEDMETHOD, DELETEMETHOD, DIRTYMETHOD, SMARTCOLLECTION, SMARTCONTAINERS, SMARTLIST, SMARTMAP, SMARTSET, VERSIONCLASS, VERSIONFIELD, VERSIONHELPER, VERSIONINTERFACE, VERSIONMETHOD
 
Constructor Summary
JavaAssistInstrumentor()
          Creates new instance.
JavaAssistInstrumentor(java.lang.Class clazz)
          Added for convenience.
JavaAssistInstrumentor(java.lang.String name)
          The instrumentor is initialized by this constructor.
 
Method Summary
private static void addDelegations(javassist.CtClass iface, javassist.CtField field, javassist.CtClass declaring)
          Generic method to implement an interface by delegation.
private static void addFieldInterceptor(javassist.CtField field, java.util.Properties ifaces)
          Adds an interceptor to a field (for write access).
private static void addFieldInterceptors(javassist.CtClass cc)
          Adds interceptors to all fields declared in cc.
private static javassist.CtField addVersionField(javassist.CtClass cc)
          Adds a version field to the class.
private  boolean alreadyModified(javassist.CtClass ctclass)
          Prevents class from being instrumented twice.
private static javassist.CtMethod createTrapWrite(javassist.CtClass cc)
          Creates a 'trap' for interception.
private static javassist.CtMethod createTrapWriteGeneric(javassist.CtClass cc, java.lang.String dumb, java.lang.String smart)
          To replace assignment to specific interfaces by a wrapped version.
private static javassist.CtField createVersionField(javassist.CtClass declaring)
          Creates the version field.
 java.lang.Class defineClass()
          Use internal classloader to build class object.
private static java.lang.String fieldWrite(java.lang.String name)
          Convention to name methods.
 byte[] getBytecode()
          Get modified class as byte array.
static java.lang.ClassLoader getClassLoader()
          For testing.
static java.lang.Class loadAndDefine(java.lang.String name)
          For testing.
private static void makeFieldsPublic(javassist.CtClass cc)
          Makes fields public.
private static void makeVersionable(javassist.CtClass cc)
          Makes class versionable.
private static javassist.CtClass modifyClass(javassist.CtClass cc)
          Applies all necessary modifications to make class versionable.
 void modifyClass(java.lang.String name)
          Modifies this class.
 void modifyClass(java.lang.String basedir, java.lang.String file)
           
static java.lang.Class smartify(java.lang.Class clazz)
          Static convenience method.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

log

private static final org.apache.commons.logging.Log log

pool

private static final javassist.ClassPool pool
Default pool to obtain CtClasses from.


converter

private static final javassist.CodeConverter converter
The codeconverter to be used to change field access.


instCL

private static JavaAssistInstrumentor.InstClassLoader instCL
Custom classloader to define classes at runtime.


ctclass

private javassist.CtClass ctclass
A The modified class in javassist's representation.

Constructor Detail

JavaAssistInstrumentor

public JavaAssistInstrumentor()
Creates new instance. Use modifyClass() to do the actual modification.


JavaAssistInstrumentor

public JavaAssistInstrumentor(java.lang.String name)
                       throws InstrumentorException
The instrumentor is initialized by this constructor.

Parameters:
name - class to be modified, either as path or in package notation.
Throws:
InstrumentorException - when encountering problems while loading/ modifying the class.

JavaAssistInstrumentor

public JavaAssistInstrumentor(java.lang.Class clazz)
                       throws InstrumentorException
Added for convenience.

Parameters:
clazz - to be modified
Throws:
InstrumentorException
Method Detail

modifyClass

public void modifyClass(java.lang.String name)
                 throws InstrumentorException
Description copied from interface: Instrumentor
Modifies this class.

Specified by:
modifyClass in interface Instrumentor
Parameters:
name - class to modify, package notation or filename.
Throws:
InstrumentorException - in case of errors

modifyClass

public void modifyClass(java.lang.String basedir,
                        java.lang.String file)
                 throws InstrumentorException
Specified by:
modifyClass in interface Instrumentor
InstrumentorException

getBytecode

public byte[] getBytecode()
                   throws InstrumentorException
Description copied from interface: Instrumentor
Get modified class as byte array.

Specified by:
getBytecode in interface Instrumentor
Returns:
versionable class as bytecode.
Throws:
InstrumentorException

defineClass

public java.lang.Class defineClass()
Description copied from interface: Instrumentor
Use internal classloader to build class object.

Exists rather for testing purposes, as classes won't be compatible !

Specified by:
defineClass in interface Instrumentor
Returns:
Versionable class.

smartify

public static java.lang.Class smartify(java.lang.Class clazz)
                                throws InstrumentorException
Static convenience method. To be used as low overhead instrumentor, e.g. for runtime modifications.

Parameters:
clazz - to be modified
Returns:
versionable class
Throws:
InstrumentorException

loadAndDefine

public static java.lang.Class loadAndDefine(java.lang.String name)
                                     throws java.lang.ClassNotFoundException
For testing.

java.lang.ClassNotFoundException

getClassLoader

public static java.lang.ClassLoader getClassLoader()
For testing.


modifyClass

private static javassist.CtClass modifyClass(javassist.CtClass cc)
                                      throws InstrumentorException
Applies all necessary modifications to make class versionable.

Parameters:
cc - class to be modified.
Returns:
modified class.
InstrumentorException

makeFieldsPublic

private static void makeFieldsPublic(javassist.CtClass cc)
Makes fields public.

Parameters:
cc -

makeVersionable

private static void makeVersionable(javassist.CtClass cc)
                             throws javassist.CannotCompileException,
                                    javassist.NotFoundException,
                                    InstrumentorException
Makes class versionable. Adds version field, implements VERSIONINTERFACE by delegating all the methods to it.

javassist.CannotCompileException
javassist.NotFoundException
InstrumentorException
See Also:
Instrumentor.VERSIONINTERFACE, Instrumentor.VERSIONFIELD

addDelegations

private static void addDelegations(javassist.CtClass iface,
                                   javassist.CtField field,
                                   javassist.CtClass declaring)
                            throws InstrumentorException,
                                   javassist.NotFoundException,
                                   javassist.CannotCompileException
Generic method to implement an interface by delegation.

E.g. declaring.isDirty() ==> declaring.field.isDirty().

Parameters:
iface - interface to implement.
field - field to delegate to.
declaring - class to add interface to.
InstrumentorException
javassist.NotFoundException
javassist.CannotCompileException

addFieldInterceptors

private static void addFieldInterceptors(javassist.CtClass cc)
                                  throws javassist.NotFoundException,
                                         javassist.CannotCompileException
Adds interceptors to all fields declared in cc.

javassist.NotFoundException
javassist.CannotCompileException

addFieldInterceptor

private static void addFieldInterceptor(javassist.CtField field,
                                        java.util.Properties ifaces)
                                 throws javassist.NotFoundException,
                                        javassist.CannotCompileException
Adds an interceptor to a field (for write access). Implemented by 'wrapping around' a trap method for type safety. Because field interception can only be intercepted by calling a static method we need to implement a static wrapper called 'trap' which will finally do an interception on the object itself.

write access to field foo of type Bar will result in: static write_foo(Object o, Bar bar) { trap_method };

Parameters:
field - field to be intercepted.
ifaces - interfaces with their 'smart' replacements.
javassist.NotFoundException
javassist.CannotCompileException

fieldWrite

private static java.lang.String fieldWrite(java.lang.String name)
Convention to name methods.


addVersionField

private static javassist.CtField addVersionField(javassist.CtClass cc)
                                          throws javassist.CannotCompileException,
                                                 javassist.NotFoundException
Adds a version field to the class.

Parameters:
cc - target class.
Returns:
the version field.
Throws:
javassist.CannotCompileException
javassist.NotFoundException
See Also:
Instrumentor.VERSIONFIELD, createVersionField(javassist.CtClass declaring)

createVersionField

private static javassist.CtField createVersionField(javassist.CtClass declaring)
                                             throws javassist.CannotCompileException,
                                                    javassist.NotFoundException
Creates the version field.

javassist.CannotCompileException
javassist.NotFoundException
See Also:
addVersionField(javassist.CtClass cc)

createTrapWrite

private static javassist.CtMethod createTrapWrite(javassist.CtClass cc)
                                           throws javassist.CannotCompileException
Creates a 'trap' for interception. The created 'trap' will call VERSIONMETHOD (e.g. 'touch') on the object and set the field using reflection. In case of fields in the java.lang.* package or primitive types we do an invocation of the equals method to verify if a real change has taken place or if the field already contains the value. In this case the object will not be marked as 'dirty'.

Parameters:
cc - target class.
Returns:
trap method.
javassist.CannotCompileException
See Also:
addFieldInterceptor(javassist.CtField, java.util.Properties)

createTrapWriteGeneric

private static javassist.CtMethod createTrapWriteGeneric(javassist.CtClass cc,
                                                         java.lang.String dumb,
                                                         java.lang.String smart)
                                                  throws javassist.CannotCompileException
To replace assignment to specific interfaces by a wrapped version. This allows for Collections doing versionable transactions (remove,...). Assignments to fields of type 'dumb' are replaced by new SmartXXX(dumb, versionable).

By using 'versionable' as the second parameter, the newly created object gets a reference to the version state of its parent.

Parameters:
cc - target class.
dumb - package name of class to replace.
smart - package name of replacing class.
Returns:
trap method.
javassist.CannotCompileException

alreadyModified

private boolean alreadyModified(javassist.CtClass ctclass)
                         throws javassist.NotFoundException
Prevents class from being instrumented twice.

javassist.NotFoundException