【问题标题】:Scala / Clojure interop works on a PC but fails on another with java.lang.ExceptionInInitializerErrorScala / Clojure 互操作可在 PC 上运行,但在另一台 PC 上因 java.lang.ExceptionInInitializerError 而失败
【发布时间】:2012-03-26 17:33:35
【问题描述】:

我有一个混合使用 Java/Scala 编写的项目,我在其中调用了由 Clojure 生成的类 (ca.gsimard.spacecraft.client.clojail) 公开的静态方法 (epadEval)。

在 clojail.clj 中:
(用 Leinengen 编译成一个独立的 jar:“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)))

在 main.scala 中:
(在我导入 Leinengen 之前生成的 .jar 的 Eclipse 项目中):

import ca.gsimard.spacecraft.client.clojail
println("Epad: " + clojail.epadEval("(+ 1 2 3)"))

我通过构建一个 fat jar 来部署项目并运行它:

在 PC1 (Linux) 上:

Epad: 6

在 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

我对正在发生的事情一无所知:我所知道的只是发生的事情并不多。看起来 Clojure 类甚至没有被加载。在 (ns..) 和 (defn..) 之间添加 (println ..) 命令不会在 PC2 上打印任何内容,因此问题似乎出在加载时,而不是在调用时。

请注意,在失败的同一台 Windows7 计算机上,我可以使用 (-main) 方法调用 (-epadEval..) 成功构建和运行仅限 Clojure 的独立 uberjar。

知道这里发生了什么吗?

编辑:我已经按照下面的建议使用 java -verbose 运行了这个。据我了解,函数 epadEval 在定义之前就被调用了! JVM 仍在加载 clojure.core 时发生异常。在此之前我没有看到任何 [Loaded ca.gsimard.spacecraft.client.clojail...]

[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]

在任何人问之前,是的,这个应用程序中有多个线程(使用 Akka Actors),是的,对 epadEval 的调用是从这样一个 Actor 的 receive 函数完成的。运行 Windows 7(崩溃)的 PC 比我运行 Linux 的 2 核笔记本电脑(不会崩溃)具有更多内核。我的猜测是,我现在对笔记本电脑上的线程一直很幸运。

  • 这个猜测有意义吗?
  • 如何确保 clojail 类在某些线程尝试调用其静态函数之一之前完全加载?

【问题讨论】:

标签: java scala clojure interop leiningen


【解决方案1】:

正如另一个答案所述,这几乎可以肯定是环境差异。

像这样以详细模式运行java:

java -verbose -jar project.jar

将提供有关类加载的大量信息。运气好的话,您将能够推断出一些有用的信息,即在异常发生之前立即加载了哪些类。

【讨论】:

    【解决方案2】:

    Akka 使用当前线程(创建 ActorSystem 的线程)上下文类加载器(如果有)。可以在这里玩吗?

    【讨论】:

    • 我同时用 Maven3(这是一个 Scala/Java/Clojure 项目)重建了所有东西,而不是先用 Leiningen 打包一个 clojure jar,然后用 sbt 构建其余部分。我没有问题了。我很失望,我无法确定这背后的真正问题,但我猜它与调用线程有关,没有确保事先完全加载了 clojure jar 部分。
    【解决方案3】:

    99% 的情况下,这些问题是由于一台机器与另一台机器的环境差异造成的。你没有提到 IDE(如果有的话 - 是 Eclipse 吗?)

    检查您的 JVM 版本并确保您使用的是您认为的 JVM,这一点很重要。例如,您可以在 Linux 上使用 openJDK,在 Windows 上使用 Oracle JDK。 在两台机器上运行 java --version 来检查你的配置。

    请发布有关您的配置的更多信息,以便我们提供进一步帮助。

    【讨论】:

    • 在 main.scala 中:(在 Eclipse 项目中,我导入 Leinengen 之前生成的 .jar)感谢有关 JVM 版本的建议:我将发布更多信息关于今晚的两台机器,其中一台我现在无法访问。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多