Override nested class at runtime, is it possible? (java.lang.NoClassDefFoundError: class names do not match)
I have a test class called "InnerClassTransactionServlet", inside this class I have defined an inner class called RandomResultHandler (InnerClassTransactionServlet $ RandomResultHandler).
I'm trying to override an inner class dynamically, but the toolkit returns a "class names do not match" error:
java.lang.NoClassDefFoundError: class names don't match
at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
at com.example.Test.redefineClass(Test.java:33)
.
.
.
I tried to override the include class but I get the same error.
The method responsible for overriding is very simple:
public static void redefineClass(ClassDefinition definition) throws ClassNotFoundException, UnmodifiableClassException{
log.debug("Trying to redefine class "+definition.getDefinitionClass().getName());
Agent.getInstrumentation().redefineClasses(definition);
}
The "definition" parameter is just a ClassDefinition object, defined with the class name and the compilation result byte:
log.debug("Class "+clazz.getName()+" compiled properly. Trying to replace the bytecode in memory.");
ClassDefinition cd = new ClassDefinition(clazz, compilationResult);
redefineClass(cd);
Using exactly the same code to override non-nested classes (or classes without nested classes) it works great.
Do you know if it is possible to override nested classes or classes that contain nested classes?
Thank!!
Finally, I was able to find the problem :)
It is necessary to override nested classes at the same time except the attached one. It is not possible to override just a nested class.
So, I changed the redefinition method to get multiple ClassDefinition objects:
public static void redefineClasses(ClassDefinition... definition) throws ClassNotFoundException, UnmodifiableClassException{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < definition.length; i++) {
if ( sb.length() > 0 )
sb.append(", ");
sb.append(definition[i].getDefinitionClass().getName());
}
log.debug("Trying to redefine class"+(definition.length>1?"es ":" ")+sb);
Agent.getInstrumentation().redefineClasses(definition);
}
So now to override my inner class, I call this method with the class definition "InnerClassTransactionServlet" and "InnerClassTransactionServlet $ RandomResultHandler" and the code has been replaced correctly.