Details
-
Type:
Defect
-
Status:
Open
-
Priority:
Major
-
Resolution: Unresolved
-
Affects Version/s: Release 1.4, Release 1.5
-
Fix Version/s: None
-
Component/s: None
-
Labels:None
-
Environment:Tested on Mac OS X 10.8 and Oracle JVM 1.7.0 update 13.
-
Patch:Code
-
Approval:Vetted
Description
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.
Exception in thread "main" java.lang.ExceptionInInitializerError
at java.io.ObjectStreamClass.hasStaticInitializer(Native Method)
at java.io.ObjectStreamClass.computeDefaultSUID(ObjectStreamClass.java:1723)
at java.io.ObjectStreamClass.access$100(ObjectStreamClass.java:69)
at java.io.ObjectStreamClass$1.run(ObjectStreamClass.java:247)
at java.io.ObjectStreamClass$1.run(ObjectStreamClass.java:245)
at java.security.AccessController.doPrivileged(Native Method)
at java.io.ObjectStreamClass.getSerialVersionUID(ObjectStreamClass.java:244)
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:600)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1601)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:324)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:173)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
at $Proxy0.getResult(Unknown Source)
at client.SampleClient$_main.doInvoke(SampleClient.clj:12)
at clojure.lang.RestFn.invoke(RestFn.java:397)
at clojure.lang.AFn.applyToHelper(AFn.java:159)
at clojure.lang.RestFn.applyTo(RestFn.java:132)
at client.SampleClient.main(Unknown Source)
Caused by: java.io.FileNotFoundException: Could not locate remoteserver/SampleInterfaceImpl__init.class or remoteserver/SampleInterfaceImpl.clj on classpath:
at clojure.lang.RT.load(RT.java:434)
at clojure.lang.RT.load(RT.java:402)
at clojure.core$load$fn__5039.invoke(core.clj:5520)
at clojure.core$load.doInvoke(core.clj:5519)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.lang.Var.invoke(Var.java:415)
at remoteserver.SampleInterfaceImpl.<clinit>(Unknown Source)
... 23 more
HOW TO REPRODUCT THIS ISSUE
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):
cd clojure_genclass_fix sh build.sh
start rmiregistry:
rmiregistry &
start remoteserver:
cd remoteserver sh start.sh
You will see a message "Server ready. " or "Server ready. (rebind)".
At last, start client program:
cd ../client sh start.sh
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
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 )
static { RT.var("clojure.core", "load").invoke("/remoteserver/SampleInterfaceImpl"); }
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.
static { Var.pushThreadBindings(RT.map(new Object[] { Compiler.LOADER, SampleInterfaceImpl.class.getClassLoader() })); try { RT.var("clojure.core", "load").invoke("/remoteserver/SampleInterfaceImpl"); } finally { Var.popThreadBindings(); } }
With this code, RT.load will uses a same classloader which load SampleInterfaceImpl.class.
My patch for gen-class will create bytecodes equal to the above example.
You can use an attached patch '20130204_fix_classloader.diff', or pull 'fix_classloader' branch from my github repositry ( git@github.com:tyano/clojure.git ).
Attachments
Activity
| Field | Original Value | New Value |
|---|---|---|
| Description |
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. Exception in thread "main" java.lang.ExceptionInInitializerError at java.io.ObjectStreamClass.hasStaticInitializer(Native Method) at java.io.ObjectStreamClass.computeDefaultSUID(ObjectStreamClass.java:1723) at java.io.ObjectStreamClass.access$100(ObjectStreamClass.java:69) at java.io.ObjectStreamClass$1.run(ObjectStreamClass.java:247) at java.io.ObjectStreamClass$1.run(ObjectStreamClass.java:245) at java.security.AccessController.doPrivileged(Native Method) at java.io.ObjectStreamClass.getSerialVersionUID(ObjectStreamClass.java:244) at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:600) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1601) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369) at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:324) at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:173) at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194) at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148) at $Proxy0.getResult(Unknown Source) at client.SampleClient$_main.doInvoke(SampleClient.clj:12) at clojure.lang.RestFn.invoke(RestFn.java:397) at clojure.lang.AFn.applyToHelper(AFn.java:159) at clojure.lang.RestFn.applyTo(RestFn.java:132) at client.SampleClient.main(Unknown Source) Caused by: java.io.FileNotFoundException: Could not locate remoteserver/SampleInterfaceImpl__init.class or remoteserver/SampleInterfaceImpl.clj on classpath: at clojure.lang.RT.load(RT.java:434) at clojure.lang.RT.load(RT.java:402) at clojure.core$load$fn__5039.invoke(core.clj:5520) at clojure.core$load.doInvoke(core.clj:5519) at clojure.lang.RestFn.invoke(RestFn.java:408) at clojure.lang.Var.invoke(Var.java:415) at remoteserver.SampleInterfaceImpl.<clinit>(Unknown Source) ... 23 more HOW TO REPRODUCT THIS ISSUE 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): cd clojure_genclass_fix sh build.sh start rmiregistry: rmiregistry & start remoteserver: cd remoteserver sh start.sh You will see a message "Server ready. " or "Server ready. (rebind)". At last, start client program: cd ../client sh start.sh 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 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 ) static { RT.var("clojure.core", "load").invoke("/remoteserver/SampleInterfaceImpl"); } 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. static { Var.pushThreadBindings(RT.map(new Object[] { Compiler.LOADER, SampleInterfaceImpl.class.getClassLoader() })); try { RT.var("clojure.core", "load").invoke("/remoteserver/SampleInterfaceImpl"); } finally { Var.popThreadBindings(); } } With this code, RT.load will uses a same classloader which load SampleInterfaceImpl.class. My patch for gen-class will create bytecodes equal to the above example. You can use an attached patch '20130204_fix_classloader.diff', or pull 'fix_classloader' branch from my github repositry ( git@github.com:tyano/clojure.git ). |
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. {code:java} Exception in thread "main" java.lang.ExceptionInInitializerError at java.io.ObjectStreamClass.hasStaticInitializer(Native Method) at java.io.ObjectStreamClass.computeDefaultSUID(ObjectStreamClass.java:1723) at java.io.ObjectStreamClass.access$100(ObjectStreamClass.java:69) at java.io.ObjectStreamClass$1.run(ObjectStreamClass.java:247) at java.io.ObjectStreamClass$1.run(ObjectStreamClass.java:245) at java.security.AccessController.doPrivileged(Native Method) at java.io.ObjectStreamClass.getSerialVersionUID(ObjectStreamClass.java:244) at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:600) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1601) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369) at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:324) at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:173) at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194) at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148) at $Proxy0.getResult(Unknown Source) at client.SampleClient$_main.doInvoke(SampleClient.clj:12) at clojure.lang.RestFn.invoke(RestFn.java:397) at clojure.lang.AFn.applyToHelper(AFn.java:159) at clojure.lang.RestFn.applyTo(RestFn.java:132) at client.SampleClient.main(Unknown Source) Caused by: java.io.FileNotFoundException: Could not locate remoteserver/SampleInterfaceImpl__init.class or remoteserver/SampleInterfaceImpl.clj on classpath: at clojure.lang.RT.load(RT.java:434) at clojure.lang.RT.load(RT.java:402) at clojure.core$load$fn__5039.invoke(core.clj:5520) at clojure.core$load.doInvoke(core.clj:5519) at clojure.lang.RestFn.invoke(RestFn.java:408) at clojure.lang.Var.invoke(Var.java:415) at remoteserver.SampleInterfaceImpl.<clinit>(Unknown Source) ... 23 more {code} h2. HOW TO REPRODUCT THIS ISSUE 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): {code:none} cd clojure_genclass_fix sh build.sh {code} start rmiregistry: {code:none} rmiregistry & {code} start remoteserver: {code:none} cd remoteserver sh start.sh {code} You will see a message "Server ready. " or "Server ready. (rebind)". At last, start client program: {code:none} cd ../client sh start.sh {code} 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." h2.THE REASON 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 ) {code:java} static { RT.var("clojure.core", "load").invoke("/remoteserver/SampleInterfaceImpl"); } {code} 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. {code:java} static { Var.pushThreadBindings(RT.map(new Object[] { Compiler.LOADER, SampleInterfaceImpl.class.getClassLoader() })); try { RT.var("clojure.core", "load").invoke("/remoteserver/SampleInterfaceImpl"); } finally { Var.popThreadBindings(); } } {code} With this code, RT.load will uses a same classloader which load SampleInterfaceImpl.class. My patch for gen-class will create bytecodes equal to the above example. You can use an attached patch '20130204_fix_classloader.diff', or pull 'fix_classloader' branch from my github repositry ( git@github.com:tyano/clojure.git ). |
| Approval | Triaged [ 10120 ] |
| Approval | Triaged [ 10120 ] | Vetted [ 10003 ] |