Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integrating javassist byte code manipulation with maven compilation

I have a maven project which compiles with javac / aspectj compiler.
I want to run on classes which were compiled a javassist program which manipulate the compiled classes and add stuff to them.
I thought using the "process-classes" phase to run my tool.
My question is what is the best way to iterate with javassist over the classes files created in the "target/classes" so I can load, fix and save afterwards.
Another requirement is to run the tool on test classes as well.
If there is an open source project which does similar stuff it will be great to see a live example.
Thanks,
Avner

like image 289
Avner Levy Avatar asked Sep 14 '12 10:09

Avner Levy


1 Answers

I stumbled upon the same problem recently and I wrote a small Maven Plugin to apply Javassist class transformations during build time. I've shared the code on https://github.com/drochetti/javassist-maven-plugin

You've guessed right, you should use the process-classes phase and the tricky part is the classpath setup. After some trials and errors I've managed to guess the whole ClassPath problem among Project, Dependencies and Javassist (please, refer to com.github.drochetti.javassist.maven.JavassistMojo.execute() code if you want to check the solution).

There're some guidelines on GitHub link above, but basically you need to:

1 - Configure the plugin on your pom.xml

<plugin>
    <groupId>com.github.drochetti</groupId>
    <artifactId>javassist-maven-plugin</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <configuration>
        <includeTestClasses>false</includeTestClasses>
        <transformerClasses>
            <transformerClass>com.domain.ToStringTransformer</transformerClass>
        </transformerClasses>
    </configuration>
    <executions>
        <execution>
            <phase>process-classes</phase>
            <goals>
                <goal>javassist</goal>
            </goals>
        </execution>
    </executions>
</plugin>

2 - Implement a ClassTransformer, here's an example:

/**
 * Silly transformer, used to hack the toString method.
 */
public class ToStringTransformer extends ClassTransformer {

    /**
     * We'll only transform subtypes of MyInterface.
     */
    @Override
    protected boolean filter(CtClass candidateClass) throws Exception {
        CtClass myInterface = ClassPool.getDefault().get(MyInterface.class.getName());
        return !candidateClass.equals(myInterface) && candidateClass.subtypeOf(myInterface);
    }

    /**
     * Hack the toString() method.
     */
    @Override
    protected void applyTransformations(CtClass classToTransform) throws Exception {
        // Actually you must test if it exists, but it's just an example...
        CtMethod toStringMethod = classToTransform.getDeclaredMethod("toString");
        classToTransform.removeMethod(toStringMethod);

        CtMethod hackedToStringMethod = CtNewMethod.make(
                "public String toString() { return \"toString() hacked by Javassist\"; }",
                classToTransform);
        classToTransform.addMethod(hackedToStringMethod);
    }

}

Note: to implement a transformer you'll need to add the plugin as a dependency of your project, but don't worry as it's only used during build time it can be provided scoped, this way it'll not be a dependency of your final build.

I hope that helps! Let me know if you need further help.

Daniel

like image 110
Daniel Rochetti Avatar answered Nov 14 '22 00:11

Daniel Rochetti