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;
}
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.