Lein 无法将罐子添加到 uberjar

Lein fails to add jars to uberjar

我正在构建一个简单的 Web 应用程序,它依赖于几个预编译的 jar,我想将其部署在 Heroku 上。我将罐子放入 resources 文件夹并将以下行添加到我的 project.clj

:resource-paths ["resources/jsesh-6.5.5.jar"
                 "resources/jseshGlyphs-6.5.5.jar"
                 "resources/java-cup-11b-runtime.jar"
                 "resources/java-cup-11b.jar"
                 "resources/qenherkhopeshefUtils-6.5.5.jar"]

现在我可以 运行 使用 lein run -m hieroglyphs.web 的项目;但是,当我用 lein uberjar 编译所有内容并尝试

java -cp ./target/hieroglyphs-standalone.jar clojure.main -m hieroglyphs.web

程序启动,但随后在尝试访问这些 jars 中定义的 类 之一时崩溃并显示 java.lang.NoClassDefFoundError

java.lang.NoClassDefFoundError: jsesh/mdcDisplayer/preferences/DrawingSpecification

我是否应该做一些额外的事情,以便编译后可以访问 jars 中定义的 类?

所有代码都可以在这里找到:https://github.com/macleginn/jsesh-web

仅仅拥有一个资源目录并没有帮助。您将需要一个适当的本地 Maven 存储库。幸运的是,制作一个并不难。

步骤:

  1. Install Maven 如果需要
  2. 在源存储库中创建一个 lib 文件夹
  3. :repositories {"local" "file:lib"} 添加到您的 project.clj
  4. 对于您的每个依赖项 运行 类似 mvn deploy:deploy-file -Dfile=resources/jsesh-6.5.5.jar -DartifactId=jsesh -Dversion=6.5.5 -DgroupId=jsesh -Dpackaging=jar -Durl=file:lib 的东西(适用于 jsesh jar)。请特别注意 artifactIdgroupIdversion
  5. 为每个罐子添加适当的 :dependencies 项目。例如对于我在第 4 步中所做的那个,[jsesh/jsesh "6.5.5"]
  6. 为每个依赖项重复步骤 4 和 5

您需要将 lib 文件夹提交到源代码管理,但现在可以删除 resources 文件夹和 project.clj[=25 中的 :resource-paths 位=]

(主要基于 https://gist.github.com/stuartsierra/3062743 的注释)

您应该更改 java 调用以使用 -jar 选项,如下所示:

~/expr/rundir > java -jar ./calc-0.1.0-SNAPSHOT-standalone.jar 
main - enter
  (ac/add2 3 5) => 8
main - leave

对于看起来像这样的代码:

calc
├── project.clj
├── resources
│   └── adder.jar
└── src
    ├── calc
    │   └── core.clj

~/expr/calc > cat src/calc/core.clj 
(ns calc.core
  "Contains the core functions for namespace `calc.core`."
  (:require [adder.core :as ac] )
  (:gen-class))

(defn -main []
  (println "main - enter")
  (println (ac/add2 3 5))
  (println "main - leave"))

文件 adder.jar 是使用来自另一个项目的 lein jar 创建的,具有单一功能:

(ns adder.core)
(defn add2 [x y]
  (+ x y))

生成的文件已重命名为 "adder.jar" 并放入 calc 项目的 resources 目录中。查看 project.clj:

(defproject calc  "0.1.0-SNAPSHOT"
  :description    "FIXME: write description"
  :url            "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url  "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [
    [org.clojure/clojure "1.9.0"]
    [org.clojure/test.check "0.9.0"]
    [prismatic/schema "1.1.7"]
    [tupelo "0.9.71"]
  ]
  :profiles {:dev {:dependencies []
                   :plugins [
                     [com.jakemccrary/lein-test-refresh "0.22.0"] ] }
             :uberjar {:aot :all}}
  :global-vars {*warn-on-reflection* false}
  :main ^:skip-aot calc.core

  :source-paths       ["src"]
  :test-paths         ["src"]
  :resource-paths     ["resources/adder.jar"]
  :target-path        "target/%s"
  :jvm-opts           ["-Xms500m" "-Xmx2g"]
)

技巧就在 :resource-paths 的行中,像这样:

:resource-paths     [ "resources/adder.jar" ]

您需要将每个 *.jar 文件单独列出 作为向量中的字符串 。请注意以下将不起作用:

:resource-paths     [ "resources" ]            ; does not find *.jar files
:resource-paths     [ "resources/*.jar" ]      ; wildcards do not work
:resource-paths     [  resources/adder.jar ]   ; without quotes fails

然后我们可以为 calc 项目创建一个 uberjar,其中将包含来自 adder.jar:

的内容
~/expr/calc > lein uberjar
Compiling _bootstrap
Compiling calc.core
Compiling tst.calc.core
Created /home/alan/expr/calc/target/uberjar/calc-0.1.0-SNAPSHOT.jar
Created /home/alan/expr/calc/target/uberjar/calc-0.1.0-SNAPSHOT-standalone.jar

您想为您的 uberjar 使用 *-standalone.jar 版本。我们将它复制到一个空目录以验证它是否有效:

~/expr/calc > mkdir -p ../rundir
~/expr/calc > cp target/uberjar/calc-0.1.0-SNAPSHOT-standalone.jar ../rundir

~/expr/calc > cd ../rundir
~/expr/rundir > ls -al
total 11744
drwxrwxr-x 2 alan alan     4096 Jan 14 19:22 .
drwxrwxr-x 5 alan alan     4096 Jan 14 19:22 ..
-rw-rw-r-- 1 alan alan 12016027 Jan 14 19:30 calc-0.1.0-SNAPSHOT-standalone.jar

~/expr/rundir > java -jar calc-0.1.0-SNAPSHOT-standalone.jar 
main - enter
  (ac/add2 3 5) => 8
main - leave