0
votes

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 ?
3
This looks suspiciously like dev.clojure.org/jira/browse/CLJ-260. Perhaps your scala code is playing with classloaders? Also see: stackoverflow.com/questions/9814005/…sw1nn
No I'm not playing with class loaders, luckily for me.gsimard

3 Answers

2
votes

As noted in another answer, this is almost certainly environment differences.

Running java in verbose mode like this:

java -verbose -jar project.jar

will give lots of information about classes loading. With some luck you will be able to infer some useful information from which classes were loading immediately before the exception occurred.

0
votes

99% of the time these problems are due to environmental differences from one machine to the other. You don't mention the IDE (if any - is it Eclipse?)

Checking your JVM versions and making sure you are using the JVM you think you are is important. For example, you could be using openJDK on Linux, and the Oracle JDK on Windows. Run java --version on both machines to check your configuration.

Please post more information on your configuration so we can help further.

0
votes

Akka uses the current Thread's (the Thread creating the ActorSystem) context classloader (if any). Can that play in here?