Search code examples
javabytecodeinstrumentationjavassist

javassist.CannotCompileException: [source error] no such class: TestDebug


I am trying to add a try catch block using bytecode instrumentation.

So basically, I have a class 'Lion'. I want to capture whole content of one of its method 'stayLion()' in try block and give a corresponding catch.

For this I have made use of method.addCatch() in transform method. Things are working fine, except for when I try to add this statement in the catch block 'TestDebug test = new TestDebug()'. I am not able to create an object of a my own defined class.

Also, is there any other simpler process to capture method body in try catch block using BCI?

package com.javapapers.java.instrumentation;

public class TestInstrumentation {
    public static void main(String args[]) throws InterruptedException {
        Lion l = new Lion();
        l.runLion();
        l.stayLion();
        System.out.println("The program has ended!");
    }
}

package com.javapapers.java.instrumentation;

import java.io.Serializable;

//to be instrumented java class
public class Lion implements Serializable{
    public static int counter =0;
    public String testing = "This is just to create an object. This is a final testing!!!";
    public void runLion() throws InterruptedException {
        counter++;
        System.out.println("Lion is going to run........!!!");
        Thread.sleep(2000L);
    }


    public void stayLion() throws InterruptedException{
        counter++;
        int arr[] = {1,2,3,4};
        System.out.println("The element is " + arr[4]);         //this error is done intentionally to have the catch block implemented
        System.out.println("Lion is going to stay.......!!!");
        Thread.sleep(2000L);
    }

}

package com.javapapers.java.instrumentation;

import java.io.ByteArrayInputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

//this class will be registered with instrumentation agent
public class DurationTransformer implements ClassFileTransformer {
    public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined,
            ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        byte[] byteCode = classfileBuffer;


        if (className.equals("com/javapapers/java/instrumentation/Lion")) {
            System.out.println("Instrumenting......");
            try {
                ClassPool classPool = ClassPool.getDefault();
                CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
                CtMethod[] methods = ctClass.getDeclaredMethods();
                for (CtMethod method : methods) {
                    method.addLocalVariable("startTime", CtClass.longType);
                    method.insertBefore("startTime = System.nanoTime();");
                    method.insertAfter("System.out.println(\"Execution Duration "
                            + "(nano sec): \"+ (System.nanoTime() - startTime) );");
                    method.insertAt(6, "System.out.println(\"This is a success!!!\");");
                    if(method.getName().equals("stayLion"))
                    {   
                        CtClass etype = ClassPool.getDefault().get("java.lang.Exception");
                        method.addCatch("{ System.out.println(\"We have caught the error via Transformer\"); "
                                + "TestDebug test = new TestDebug();"       //The error is here. Not able to create TestDebug object
                                + "test.saveState(this);"
                                + "throw $e; }", etype);
                    }
                }
                byteCode = ctClass.toBytecode();
                ctClass.detach();
                System.out.println("Instrumentation complete.");
            } catch (Throwable ex) {
                System.out.println("Exception: " + ex);
                ex.printStackTrace();
            }
        }
        return byteCode;
    }
}

package com.javapapers.java.instrumentation;

import java.lang.instrument.Instrumentation;

public class DurationAgent {

    // for all the class loaded, premain will be called
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("Executing premain.........");
        inst.addTransformer(new DurationTransformer());
    }
}

package com.javapapers.java.instrumentation;

import java.io.File;
import java.io.IOException;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;


public class TestDebug {

    public TestDebug(){
        System.out.println("We are in constructor");
    }

    public static void saveState(Object emp){

        ObjectMapper mapper = new ObjectMapper();
        //mapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
        try {
            mapper.writeValue(new File("savedState.json"), emp);
        } catch (JsonGenerationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JsonMappingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

================================================================================

Exception: javassist.CannotCompileException: [source error] no such class: TestDebug
javassist.CannotCompileException: [source error] no such class: TestDebug
        at javassist.CtBehavior.insertAt(CtBehavior.java:1146)
        at javassist.CtBehavior.insertAt(CtBehavior.java:1073)
        at com.javapapers.java.instrumentation.DurationTransformer.transform(DurationTransformer.java:38)
        at sun.instrument.TransformerManager.transform(Unknown Source)
        at sun.instrument.InstrumentationImpl.transform(Unknown Source)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at com.javapapers.java.instrumentation.TestInstrumentation.main(TestInstrumentation.java:5)
Caused by: compile error: no such class: TestDebug
        at javassist.compiler.MemberResolver.searchImports(MemberResolver.java:447)
        at javassist.compiler.MemberResolver.lookupClass(MemberResolver.java:412)
        at javassist.compiler.MemberResolver.lookupClassByJvmName(MemberResolver.java:330)
        at javassist.compiler.MemberResolver.resolveJvmClassName(MemberResolver.java:491)
        at javassist.compiler.MemberCodeGen.resolveClassName(MemberCodeGen.java:1146)
        at javassist.compiler.CodeGen.atDeclarator(CodeGen.java:711)
        at javassist.compiler.ast.Declarator.accept(Declarator.java:99)
        at javassist.compiler.CodeGen.atStmnt(CodeGen.java:350)
        at javassist.compiler.ast.Stmnt.accept(Stmnt.java:49)
        at javassist.compiler.Javac.compileStmnt(Javac.java:568)
        at javassist.CtBehavior.insertAt(CtBehavior.java:1125)

Solution

  • In order to use certain classes in Javaassist, you need to specify the full package names.

    Instead of

    TestDebug test = new TestDebug();

    use

    com.javapapers.java.instrumentation.TestDebug test = new com.javapapers.java.instrumentation.TestDebug();