I'm currently developing a custom ORM framework and utilising ASM to dynamically generate sub classes at runtime. The generation process seems to complete OK, however when I try to instantiate the resulting class I'm getting a "NoClassDefFoundError".
The error seems to pertain to the Super class rather then the actual subclass. Here is an excerpt from the subclass generation method:
private Class generateProxyClass(Class anEntitySuperClass,
List<Field> fieldsToIntercept) throws ClassNotFoundException{
String entitySuperClassName = this.convertToInternalName(anEntitySuperClass.getName());
//String entityProxySubClassName = "com/flux/dynamic/".concat(anEntitySuperClass.getSimpleName()).concat("Proxy");
String entityProxySubClassName = anEntitySuperClass.getSimpleName().concat("Proxy");
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(V1_6,ACC_PUBLIC+ACC_SUPER,entityProxySubClassName,null,entitySuperClassName,null);
cw.visitSource(entityProxySubClassName.concat(".java"),null);
//create constructor
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,"<init>","()V",null,null);
mv.visitCode();
//have our consturctor initailise its super class.
mv.visitVarInsn(ALOAD,0);
mv.visitMethodInsn(INVOKESPECIAL,entitySuperClassName,"<init>","()V");
mv.visitInsn(RETURN);
mv.visitMaxs(0,0);
mv.visitEnd();
this.generateAndAppendProxyAccessorMethods(anEntitySuperClass,fieldsToIntercept, cw);
cw.visitEnd();
//at this point our class should be fully generated an include all required fields. next we
//convert the class to a byte array and pass it in to our helper method to load an
//actual class from the byte array.
return this.loadProxyClass(cw.toByteArray(),entityProxySubClassName);
}
The "loadProxyClass" method called above is a helper method that basically instantiates and calls a custom ClassLoader in order to load the dynamically created class:
/**loads the generated proxy class from the provided bytes. */
private Class loadProxyClass(byte[] aGeneratedProxyClass,String proxyClassName) throws ClassNotFoundException{
return new ProxyClassLoader(Thread.currentThread().getContextClassLoader(),aGeneratedProxyClass)
.loadClass(this.convertToExternalName(proxyClassName));
}
The ProxyClassLoader simply extends ClassLoader and overrides the "findClass" method in order to load the Dynamically Generated class bytes:
public class ProxyClassLoader extends ClassLoader {
private byte[] rawClassBytes;
public ProxyClassLoader(ClassLoader parentClassLoader,byte[] classBytes){
super(parentClassLoader);
this.rawClassBytes = classBytes;
}
@Override
public Class findClass(String name) {
return defineClass(name,this.rawClassBytes, 0,this.rawClassBytes.length);
}
}
The error I get is: Exception in thread "main" java.lang.NoClassDefFoundError: DummyEntity (wrong name: DummyEntityProxy)
Where the DummyEntity
is the super class I pass into the generateProxyClass
method and the DummyEntityProxy
is the class I'm attempting to generate. I'm stumped, any help would be greatly appreciated.
Thank you all very much for your suggestions. After many hours of tinkering I managed to resolve the error. It appears that the error was attributed to the method:
More specifically, some of the code generated by this method incorrectly referenced the super class by its simple name rather than its internal fully qualified class name. I omitted the implementation of this method from my question for brevity and also because I genuinely didn't expect that the problem was associated with this method. Generally, when errors occur in dynamically generated byte code logic it can be immensely difficult to pinpoint the cause, simply because JVM error messages are so ambiguous.