Search code examples
javaeclipseruntime-errorjavassist

Javassist: javassist.CannotCompileException: by java.lang.ClassFormatError: Duplicate method name&signature in class file


I am using javassist to change a method body. I can do that when the method is defined in the application. However, when I want to change a method defined in a jar file used by the application then I get the below run time error:

javassist.CannotCompileException: by java.lang.ClassFormatError: Duplicate method name&signature in class file pk1/StringBuilder.

The below code is used to change a method body. I am using eclipse IDE. The error happens when clas.toClass(); is called.

public class JassistTiming {

public static void main(String[] arg) {

    //first parameter is name of class, and the second one is name of method
    String[] argv = {"pk1.StringBuilder","buildString"};

    // start by getting the class file and method
    CtClass clas = ClassPool.getDefault().get(argv[0]);

    // add timing interceptor to the class
    addTiming(clas, argv[1]);
    clas.writeFile();
    System.out.println("Added timing to method " + argv[0] + "." + argv[1]);

    clas.toClass(); //Run time error happens here                                             
}

private static void addTiming(CtClass clas, String mname) throws NotFoundException, CannotCompileException {

    CtMethod mold = clas.getDeclaredMethod(mname);

    String nname = mname+"$impl";
    mold.setName(nname);
    CtMethod mnew = CtNewMethod.copy(mold, mname, clas, null);

    String type = mold.getReturnType().getName();
    StringBuffer body = new StringBuffer();
    body.append("{\nlong start = System.currentTimeMillis();\n");
    if (!"void".equals(type)) {
        body.append(type + " result = ");
    }
    body.append(nname + "($$);\n");

    body.append("System.out.println(\"Call to method " + mname +
        " took \" +\n (System.currentTimeMillis()-start) + " +
        "\" ms.\");\n");
    if (!"void".equals(type)) {
        body.append("return result;\n");
    }
    body.append("}");

    mnew.setBody(body.toString());
    clas.addMethod(mnew);
}
}

Update: Code for StringBuilder:

public class StringBuilder { 

public String buildString(int length) { 

    String result = ""; 

    for (int i = 0; i < length; i++) { 
        result += (char)(i%26 + 'a'); 
    } 

    return result; 
} 

Solution

  • I believe I found the reason.

    Assuming that foobar.jar contains the class pk1.StringBuilder

    Following is working

    java -cp javassist-3.7.ga.jar:foobar.jar:. JassistTiming 
    java -cp javassist-3.7.ga.jar:foobar.jar:. JassistTiming 
    

    The instrumented class StringBuilder.class is stored in the subdirectory pk1 and the instrumented method is called.

    The following is working too

    java -cp javassist-3.7.ga.jar:.:foobar.jar JassistTiming 
    

    but fail on the second execution, because the class StringBuilder.class is now first found in the subdirectory pk1 which is the already instrumented class, so the method public void buildString$impl() already exists in this class.