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.
private  javassist.CtMethod standardTrap
          The trap method to be used for field access.
 
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  void addDelegations(javassist.CtClass iface, javassist.CtField field, javassist.CtClass declaring)
          Generic method to implement an interface by delegation.
private  void addFieldInterceptor(javassist.CtField field, java.util.Properties ifaces)
          Adds an interceptor to a field (for write access).
private  void addFieldInterceptors(javassist.CtClass cc)
          Adds interceptors to all fields declared in cc.
private  javassist.CtField addVersionField(javassist.CtClass cc)
          Adds a version field to the class.
private static boolean alreadyModified(javassist.CtClass ctclass)
          Prevents class from being instrumented twice.
private  javassist.CtMethod createTrapWrite(javassist.CtClass cc)
          Creates a 'trap' for interception.
private  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  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.
private static java.lang.String fileToClass(java.lang.String filename)
           
 byte[] getBytecode()
          Get modified class as byte array.
private  void makeFieldsPublic(javassist.CtClass cc)
          Makes fields public.
private  void makeVersionable(javassist.CtClass cc)
          Makes class versionable.
private  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)
           
private static byte[] readStream(java.io.InputStream fin)
          Helper method to read inputstream in byte array.
 
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.


standardTrap

private javassist.CtMethod standardTrap
The trap method to be used for field access.

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.

modifyClass

private 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 void makeFieldsPublic(javassist.CtClass cc)
Makes fields public.

Parameters:
cc -

makeVersionable

private 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 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 void addFieldInterceptors(javassist.CtClass cc)
                           throws javassist.NotFoundException,
                                  javassist.CannotCompileException
Adds interceptors to all fields declared in cc.

javassist.NotFoundException
javassist.CannotCompileException

addFieldInterceptor

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

readStream

private static byte[] readStream(java.io.InputStream fin)
                          throws java.io.IOException
Helper method to read inputstream in byte array.

java.io.IOException

fileToClass

private static java.lang.String fileToClass(java.lang.String filename)

alreadyModified

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

javassist.NotFoundException