[CLJ-1157] Classes generated by gen-class aren't loadable from remote codebase for mis-implementation of static-initializer Created: 04/Feb/13 Updated: 12/Apr/13
|Affects Version/s:||Release 1.4, Release 1.5|
Tested on Mac OS X 10.8 and Oracle JVM 1.7.0 update 13.
When a client program uses a remote service which uses RMI, and the service returns a object which created with gen-class with clojure as the return value, the return value is not loadable at client side.
At client side, a following exeption will be thrown.
If you want to see this issue at your computer, clone my example project from my github.
git clone git://github.com/tyano/clojure_genclass_fix.git
and build them (You must have installed Leiningen 2):
You will see a message "Server ready. " or "Server ready. (rebind)".
At last, start client program:
Without my patch, you will see a same Exception described above. But with clojure with my patch, you will see a right response message: "response = this is sample."
The reason of this problem is in bytecodes generated by gen-class. A gen-classed class (in this case, SampleInterfaceImpl.class) uses a static-initializer for loading SampleInterfaceImpl__init.class (which load other classes which implements functions in the class). The static-initializer is like bellow: (the following code is decompiled with JD - http://java.decompiler.free.fr/?q=jdgui )
Very simple code. it seems non-problematic. But RT.load changes the classloader for loading __init.class in the processing! RT.load in default uses a context-classloader for loading classes. But all classes depending on a gen-classed class must be loaded a same classloader with a main gen-classed class. In this case, RT.load must use a remote URLClassLoader which load a main class.
So, gen-class must be create bytecodes that is same with the following java code.
With this code, RT.load will uses a same classloader which load SampleInterfaceImpl.class.
You can use an attached patch '20130204_fix_classloader.diff', or pull 'fix_classloader' branch from my github repositry ( email@example.com:tyano/clojure.git ).
|Comment by Stuart Halloway [ 01/Mar/13 10:20 AM ]|
This sounds reasonable, but anything touching classloaders must be considered very carefully.
|Comment by Stuart Halloway [ 01/Mar/13 12:12 PM ]|
It seems overly complex to have the patch do so much code generation. Why not implement a method that does this job, and have the generated code call that?