I have a project written in a mixture of Java/Scala in which I call a static method (epadEval) exposed by a Clojure generated class (ca.gsimard.spacecraft.client.clojail).
In clojail.clj:
(compiled into a standalone jar with Leinengen: "lein uberjar")
(ns ca.gsimard.spacecraft.client.clojail
(:use [clojail core testers])
(:gen-class
:name ca.gsimard.spacecraft.client.clojail
:methods [#^{:static true} [epadEval [String] String]]))
(defn -epadEval
"Evaluate string s within a clojail sandbox."
[s]
(let [writer (java.io.StringWriter.)]
(*sb* (safe-read (str "(print " s ")")) {#'*out* writer})
(str writer)))
In main.scala:
(in an Eclipse project where I import the .jar generated previously by Leinengen):
import ca.gsimard.spacecraft.client.clojail
println("Epad: " + clojail.epadEval("(+ 1 2 3)"))
I deploy the project by building a fat jar and run it:
On PC1 (Linux):
Epad: 6
On PC2 (Windows 7):
Exception in thread "main" java.lang.ExceptionInInitializerError
at clojure.lang.Namespace.<init>(Namespace.java:34)
at clojure.lang.Namespace.findOrCreate(Namespace.java:176)
at clojure.lang.Var.internPrivate(Var.java:149)
at ca.gsimard.spacecraft.client.clojail.<clinit>(Unknown Source)
at ca.gsimard.spacecraft.client.Epad$.eval(EpadClient.scala:78)
at ca.gsimard.spacecraft.client.Main$.main(MainClient.scala:25)
at ca.gsimard.spacecraft.client.Main.main(MainClient.scala)
Caused by: java.lang.NullPointerException
at clojure.core$eval1697$fn__1698.invoke(core.clj:6135)
at clojure.core$eval1697.invoke(core.clj:6135)
at clojure.lang.Compiler.eval(Compiler.java:6465)
at clojure.lang.Compiler.load(Compiler.java:6902)
at clojure.lang.RT.loadResourceScript(RT.java:357)
at clojure.lang.RT.loadResourceScript(RT.java:348)
at clojure.lang.RT.load(RT.java:427)
at clojure.lang.RT.load(RT.java:398)
at clojure.lang.RT.doInit(RT.java:434)
at clojure.lang.RT.<clinit>(RT.java:316)
... 7 more
I'm quite clueless about what's happening: all I know is that not much IS happening. It looks like the Clojure class doesn't even get loaded. Adding (println ..) commands between (ns..) and (defn..) do not print anything at all on PC2, so it seems the problem is at load time, not at call time.
Note that on the same Windows7 computer where this fails, I could build and run successfully a Clojure-only standalone uberjar with a (-main) method calling (-epadEval..).
Any idea what's going on here ?
EDIT: I have had this ran with java -verbose as suggested below. From what I understand, the function epadEval gets called BEFORE it is even defined ! The exception happens while the JVM is still loading clojure.core. I did not see any [Loaded ca.gsimard.spacecraft.client.clojail...] before that.
[Loaded clojure.core$eval1697$fn__1698 from __JVM_DefineClass__]
[Loaded clojure.core$eval1697 from __JVM_DefineClass__]
Exception in thread "main" [Loaded java.lang.Throwable$PrintStreamOrWriter from
C:\Program Files\Java\jre7\lib\rt.jar]
[Loaded java.lang.Throwable$WrappedPrintStream from C:\Program Files\Java\jre7\lib\rt.jar]
[Loaded java.util.IdentityHashMap$KeySet from C:\Program Files\Java\jre7\lib\rt.jar]
java.lang.ExceptionInInitializerError
at clojure.lang.Namespace.<init>(Namespace.java:34)
at clojure.lang.Namespace.findOrCreate(Namespace.java:176)
at clojure.lang.Var.internPrivate(Var.java:149)
at ca.gsimard.spacecraft.client.clojail.<clinit>(Unknown Source)
at ca.gsimard.spacecraft.client.Epad$.eval(EpadClient.scala:78)
at ca.gsimard.spacecraft.client.Main$.main(MainClient.scala:25)
at ca.gsimard.spacecraft.client.Main.main(MainClient.scala)
[Loaded java.util.Objects from C:\Program Files\Java\jre7\lib\rt.jar]
Caused by: java.lang.NullPointerException
at clojure.core$eval1697$fn__1698.invoke(core.clj:6135)
at clojure.core$eval1697.invoke(core.clj:6135)
at clojure.lang.Compiler.eval(Compiler.java:6465)
at clojure.lang.Compiler.load(Compiler.java:6902)
at clojure.lang.RT.loadResourceScript(RT.java:357)
at clojure.lang.RT.loadResourceScript(RT.java:348)
at clojure.lang.RT.load(RT.java:427)
at clojure.lang.RT.load(RT.java:398)
at clojure.lang.RT.doInit(RT.java:434)
at clojure.lang.RT.<clinit>(RT.java:316)
... 7 more
[Loaded java.lang.Shutdown from C:\Program Files\Java\jre7\lib\rt.jar]
[Loaded java.lang.Shutdown$Lock from C:\Program Files\Java\jre7\lib\rt.jar]
Before anyone asks, yes, there are multiple threads in this app (using Akka Actors), and yes, the call to epadEval is done from the receive function of such an actor. The PC running Windows 7 (where this crashes) has more cores than my 2 cores laptop running Linux (where this doesn't crash). My guess is that I'm just being consistently lucky with the threads on my laptop right now.
- Does this guess make any sense ?
- How can I ensure the clojail class is fully loaded before some thread tries to call one of its static functions ?