[CLJ-1157] Classes generated by gen-class aren't loadable from remote codebase for mis-implementation of static-initializer Created: 04/Feb/13 Updated: 05/Feb/14
|Affects Version/s:||Release 1.4, Release 1.5, Release 1.6|
Tested on Mac OS X 10.9.1 and Oracle JVM 1.7.0_51 with Clojure 1.6 master SNAPSHOT
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 '20140121_fix_classloader.diff', or pull 'CLJ-1157' branch from my github repositry ( firstname.lastname@example.org: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?
|Comment by Andy Fingerhut [ 11/Jan/14 2:47 PM ]|
Patch 20130204_fix_classloader.diff dated Feb 3, 2013 no longer applies cleanly as of the latest commits to Clojure master on Jan 11, 2014. The only conflict in applying the patch appears to be in the file src/jvm/clojure/asm/commons/GeneratorAdapter.java. This is probably due to the commit for ticket
|Comment by Tsutomu Yano [ 21/Jan/14 3:01 AM ]|
I put a new patch applicable on the latest master branch.
And I fixed my sample program and the 'HOW TO REPRODUCT THIS ISSUE' section of this ticket, because old description is not runnable on newest JVM. It is because the specification of remote method call of the newest JVM was changed from the old one. In the newest JVM, we need a 'java.rmi.server.useCodebaseOnly=false' option for making the behavior of remote call same as old one.