jlink fails on ring-json 0.5.1

101 Views Asked by At

I have a small Clojure project with /project.clj:

(defproject testjackson "0.1.0-SNAPSHOT"
  :main testjackson.core
  :dependencies [[org.clojure/clojure "1.10.3"]
                 [ring/ring-json "0.5.0"]])

and /src/testjackson/core.clj:

(ns testjackson.core
  (:gen-class)
  (:require [ring.middleware.json :refer [wrap-json-body]]))

(defn -main [& _] (println :testing wrap-json-body))

When I run it, it just prints:

$ java -jar target/testjackson-0.1.0-SNAPSHOT-standalone.jar
:testing #object[ring.middleware.json$wrap_json_body 0x37cd92d6 ring.middleware.json$wrap_json_body@37cd92d6]

It runs as expected. I can also execute jdeps on the uberjar to get the list of used modules:

$ jdeps --print-module-deps --ignore-missing-deps target/testjackson-0.1.0-SNAPSHOT-standalone.jar
java.base,java.desktop,java.sql,jdk.unsupported

However, when I bump ring-json version to 0.5.1, the jdeps call fails:

$ jdeps --print-module-deps --ignore-missing-deps target/testjackson-0.1.0-SNAPSHOT-standalone.jar
Exception in thread "main" java.lang.module.FindException: Module com.fasterxml.jackson.core not found, required by com.fasterxml.jackson.dataformat.smile
    at java.base/java.lang.module.Resolver.findFail(Resolver.java:877)
    at java.base/java.lang.module.Resolver.resolve(Resolver.java:191)
    at java.base/java.lang.module.Resolver.resolve(Resolver.java:140)
    at java.base/java.lang.module.Configuration.resolve(Configuration.java:422)
    at java.base/java.lang.module.Configuration.resolve(Configuration.java:256)
    at jdk.jdeps/com.sun.tools.jdeps.JdepsConfiguration$Builder.build(JdepsConfiguration.java:564)
    at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.buildConfig(JdepsTask.java:603)
    at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.run(JdepsTask.java:557)
    at jdk.jdeps/com.sun.tools.jdeps.JdepsTask.run(JdepsTask.java:533)
    at jdk.jdeps/com.sun.tools.jdeps.Main.main(Main.java:49)

How can this be fixed in the context of this small example program?

1

There are 1 best solutions below

0
On

As near I as can find from searching, jdeps is from way back in Java 9. In fact, the version on my computer won't even accepts the 2 flags you used in the example, and it doesn't even have a --version option. Web searching shows no obvious uses for clojure.

What is the goal here? Your plain code works fine for me:

project.clj deps

  :dependencies [
                 [org.clojure/clojure "1.11.1"]
                 [prismatic/schema "1.2.1"]
                 [ring/ring-json "0.5.1"]
                 [tupelo "22.05.24b"]
                 ]
  :plugins [[com.jakemccrary/lein-test-refresh "0.25.0"]
            [lein-ancient "0.7.0"]
            ]

code

(ns demo.core
  (:use tupelo.core)
  (:require [ring.middleware.json :refer [wrap-json-body]])
  (:gen-class))

(defn -main [& _]
  (spyx (type wrap-json-body)))

and results

> lein clean ; lein run
(type wrap-json-body) => ring.middleware.json$wrap_json_body

Speculation

Although my jlink doesn't seem to work right, I can guess a little. Looking at project.clj, we can ask for a tree of depencencies:

> lein deps :tree
 [nrepl "0.8.3" :exclusions [[org.clojure/clojure]]]
 [org.clojure/clojure "1.11.1"]
   [org.clojure/core.specs.alpha "0.2.62"]
   [org.clojure/spec.alpha "0.3.218"]
 [org.nrepl/incomplete "0.1.0" :exclusions [[org.clojure/clojure]]]
 [prismatic/schema "1.2.1"]
 [ring/ring-json "0.5.1"]
   [cheshire "5.10.0"]
     [com.fasterxml.jackson.core/jackson-core "2.10.2"]
     [com.fasterxml.jackson.dataformat/jackson-dataformat-cbor "2.10.2"]
     [com.fasterxml.jackson.dataformat/jackson-dataformat-smile "2.10.2"]
     [tigris "0.1.2"]
   [ring/ring-core "1.9.2"]
     [commons-fileupload "1.4"]
     [commons-io "2.6"]
     [crypto-equality "1.0.0"]
     [crypto-random "1.2.0"]
     [ring/ring-codec "1.1.3"]
       [commons-codec "1.15"]

So we see that ring-json needs cheshire, which needs jackson.

Going back to ring-json 0.5.0 we see different version of cheshire and jackson:

> lein deps :tree
 [nrepl "0.8.3" :exclusions [[org.clojure/clojure]]]
 [org.clojure/clojure "1.11.1"]
   [org.clojure/core.specs.alpha "0.2.62"]
   [org.clojure/spec.alpha "0.3.218"]
 [org.nrepl/incomplete "0.1.0" :exclusions [[org.clojure/clojure]]]
 [prismatic/schema "1.2.1"]
 [ring/ring-json "0.5.0"]
   [cheshire "5.9.0"]
     [com.fasterxml.jackson.core/jackson-core "2.9.9"]
     [com.fasterxml.jackson.dataformat/jackson-dataformat-cbor "2.9.9"]
     [com.fasterxml.jackson.dataformat/jackson-dataformat-smile "2.9.9"]
     [tigris "0.1.1"]
   [ring/ring-core "1.7.1"]
     [clj-time "0.14.3"]
       [joda-time "2.9.9"]
     [commons-fileupload "1.3.3"]
     [commons-io "2.6"]
     [crypto-equality "1.0.0"]
     [crypto-random "1.2.0"]
     [ring/ring-codec "1.1.1"]
       [commons-codec "1.11"]

Note that you can force newer versions in project.clj if you want. Just manually require cheshire and jackson, and put them near the top!

  :dependencies [
                 ; priority 1 libs
                 [org.clojure/clojure "1.11.1"]
                 [cheshire "5.10.2"]
                 [com.fasterxml.jackson.core/jackson-core "2.13.3"]
                 [com.fasterxml.jackson.dataformat/jackson-dataformat-cbor "2.13.3"]
                 [com.fasterxml.jackson.dataformat/jackson-dataformat-smile "2.13.3"]

                 ; priority 2 libs
                 [prismatic/schema "1.2.1"]
                 [ring/ring-json "0.5.0"]
                 ; [tupelo "22.05.24b"]
                 ]

with result

> lcr
time (lein do clean, run)
:type ring.middleware.json$wrap_json_body
  8.11s user 0.59s system 218% cpu 3.989 total
~/expr/demo >
~/expr/demo >
~/expr/demo > lein deps :tree
 [cheshire "5.10.2"]
   [tigris "0.1.2"]
 [com.fasterxml.jackson.core/jackson-core "2.13.3"]
 [com.fasterxml.jackson.dataformat/jackson-dataformat-cbor "2.13.3"]
   [com.fasterxml.jackson.core/jackson-databind "2.13.3"]
     [com.fasterxml.jackson.core/jackson-annotations "2.13.3"]
 [com.fasterxml.jackson.dataformat/jackson-dataformat-smile "2.13.3"]
 [nrepl "0.8.3" :exclusions [[org.clojure/clojure]]]
 [org.clojure/clojure "1.11.1"]
   [org.clojure/core.specs.alpha "0.2.62"]
   [org.clojure/spec.alpha "0.3.218"]
 [org.nrepl/incomplete "0.1.0" :exclusions [[org.clojure/clojure]]]
 [prismatic/schema "1.2.1"]
 [ring/ring-json "0.5.0"]
   [ring/ring-core "1.7.1"]
     [clj-time "0.14.3"]
       [joda-time "2.9.9"]
     [commons-fileupload "1.3.3"]
     [commons-io "2.6"]
     [crypto-equality "1.0.0"]
     [crypto-random "1.2.0"]
     [ring/ring-codec "1.1.1"]
       [commons-codec "1.11"]

Perhaps something similar will solve your problem. It has been crucial for me in the past.

Specifically, the jackson libs absolutely must have all 3 sub-libraries on the same version. Because dependent libs often don't update their own deps like jackson as often as they could, it is easy to get version conflicts when 2 or more libs each require different versions of jackson. The above technique of forcing a specific version of jackson (or any other lib) is a lifesaver. IMHO, it is also much better than trying to put in a bunch of lein :exclusions clauses.