Help fixing dependency conflicts with Duct and Datomic Client Pro?

#1

Hello!

I’m facing dependency conflicts with a near minimal setup with Duct and Datomic Client. On the bad side, I’m a little lost fumbling around with :exclusions. On the bright side, Duct and Datomic are well known, and the reproduction example is small.

Reproducing

lein new duct dep-mwe +site
# Add [com.datomic/client-pro "0.8.28"] to project.clj :dependencies
cd dep-mwe
lein duct setup
lein run

Should crash with a

Exception in thread “main” Syntax error compiling . at (ring/adapter/jetty.clj:27:9).

More info follows.

What I’ve tried

Detailed error output

Full stack trace
Exception in thread "main" Syntax error compiling . at (ring/adapter/jetty.clj:27:9).
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7114)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7094)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyze(Compiler.java:6745)
	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6120)
	at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:6436)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7106)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7094)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyze(Compiler.java:6745)
	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6120)
	at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5467)
	at clojure.lang.Compiler$FnExpr.parse(Compiler.java:4029)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7104)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7094)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyze(Compiler.java:6745)
	at clojure.lang.Compiler$MapExpr.parse(Compiler.java:3104)
	at clojure.lang.Compiler.analyze(Compiler.java:6797)
	at clojure.lang.Compiler.analyze(Compiler.java:6745)
	at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3888)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7108)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyze(Compiler.java:6745)
	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6118)
	at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:6436)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7106)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7094)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7094)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyze(Compiler.java:6745)
	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6120)
	at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5467)
	at clojure.lang.Compiler$FnExpr.parse(Compiler.java:4029)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7104)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7094)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.access$300(Compiler.java:38)
	at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:596)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7106)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyze(Compiler.java:6745)
	at clojure.lang.Compiler.eval(Compiler.java:7180)
	at clojure.lang.Compiler.load(Compiler.java:7635)
	at clojure.lang.RT.loadResourceScript(RT.java:381)
	at clojure.lang.RT.loadResourceScript(RT.java:372)
	at clojure.lang.RT.load(RT.java:463)
	at clojure.lang.RT.load(RT.java:428)
	at clojure.core$load$fn__6824.invoke(core.clj:6126)
	at clojure.core$load.invokeStatic(core.clj:6125)
	at clojure.core$load.doInvoke(core.clj:6109)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5908)
	at clojure.core$load_one.invoke(core.clj:5903)
	at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
	at clojure.core$load_lib.invokeStatic(core.clj:5947)
	at clojure.core$load_lib.doInvoke(core.clj:5928)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$load_libs.invokeStatic(core.clj:5985)
	at clojure.core$load_libs.doInvoke(core.clj:5969)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$require.invokeStatic(core.clj:6007)
	at clojure.core$require.doInvoke(core.clj:6007)
	at clojure.lang.RestFn.invoke(RestFn.java:436)
	at duct.server.http.jetty$eval19486$loading__6706__auto____19487.invoke(jetty.clj:1)
	at duct.server.http.jetty$eval19486.invokeStatic(jetty.clj:1)
	at duct.server.http.jetty$eval19486.invoke(jetty.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7176)
	at clojure.lang.Compiler.eval(Compiler.java:7165)
	at clojure.lang.Compiler.load(Compiler.java:7635)
	at clojure.lang.RT.loadResourceScript(RT.java:381)
	at clojure.lang.RT.loadResourceScript(RT.java:372)
	at clojure.lang.RT.load(RT.java:463)
	at clojure.lang.RT.load(RT.java:428)
	at clojure.core$load$fn__6824.invoke(core.clj:6126)
	at clojure.core$load.invokeStatic(core.clj:6125)
	at clojure.core$load.doInvoke(core.clj:6109)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5908)
	at clojure.core$load_one.invoke(core.clj:5903)
	at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
	at clojure.core$load_lib.invokeStatic(core.clj:5947)
	at clojure.core$load_lib.doInvoke(core.clj:5928)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$load_libs.invokeStatic(core.clj:5985)
	at clojure.core$load_libs.doInvoke(core.clj:5969)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$require.invokeStatic(core.clj:6007)
	at clojure.core$require.doInvoke(core.clj:6007)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at integrant.core$try_require.invokeStatic(core.cljc:171)
	at integrant.core$try_require.invoke(core.cljc:170)
	at clojure.core$keep$fn__8544.invoke(core.clj:7341)
	at clojure.lang.LazySeq.sval(LazySeq.java:42)
	at clojure.lang.LazySeq.seq(LazySeq.java:58)
	at clojure.lang.Cons.next(Cons.java:39)
	at clojure.lang.RT.next(RT.java:709)
	at clojure.core$next__5371.invokeStatic(core.clj:64)
	at clojure.core$dorun.invokeStatic(core.clj:3142)
	at clojure.core$doall.invokeStatic(core.clj:3148)
	at clojure.core$doall.invoke(core.clj:3148)
	at integrant.core$load_namespaces.invokeStatic(core.cljc:184)
	at integrant.core$load_namespaces.invoke(core.cljc:175)
	at integrant.core$load_namespaces.invokeStatic(core.cljc:182)
	at integrant.core$load_namespaces.invoke(core.cljc:175)
	at duct.core$prep_config.invokeStatic(core.clj:194)
	at duct.core$prep_config.invoke(core.clj:184)
	at duct.core$exec_config.invokeStatic(core.clj:225)
	at duct.core$exec_config.invoke(core.clj:213)
	at dep_mwe.main$_main.invokeStatic(main.clj:12)
	at dep_mwe.main$_main.doInvoke(main.clj:7)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.lang.Var.invoke(Var.java:384)
	at user$eval151.invokeStatic(form-init5669067227873710595.clj:1)
	at user$eval151.invoke(form-init5669067227873710595.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7176)
	at clojure.lang.Compiler.eval(Compiler.java:7166)
	at clojure.lang.Compiler.load(Compiler.java:7635)
	at clojure.lang.Compiler.loadFile(Compiler.java:7573)
	at clojure.main$load_script.invokeStatic(main.clj:452)
	at clojure.main$init_opt.invokeStatic(main.clj:454)
	at clojure.main$init_opt.invoke(main.clj:454)
	at clojure.main$initialize.invokeStatic(main.clj:485)
	at clojure.main$null_opt.invokeStatic(main.clj:519)
	at clojure.main$null_opt.invoke(main.clj:516)
	at clojure.main$main.invokeStatic(main.clj:598)
	at clojure.main$main.doInvoke(main.clj:561)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:705)
	at clojure.main.main(main.java:37)
Caused by: java.lang.NoClassDefFoundError: org/eclipse/jetty/http/HttpParser$ProxyHandler
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.privateGetPublicMethods(Class.java:2902)
	at java.lang.Class.getMethods(Class.java:1615)
	at clojure.lang.Reflector.getMethods(Reflector.java:498)
	at clojure.lang.Compiler$InstanceMethodExpr.<init>(Compiler.java:1488)
	at clojure.lang.Compiler$HostExpr$Parser.parse(Compiler.java:1024)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7106)
	... 139 more
Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.http.HttpParser$ProxyHandler
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 159 more
lein run  12,41s user 0,44s system 123% cpu 10,410 total

lein deps :tree output
Possibly confusing dependencies found:
[kerodon "0.9.0"] -> [ring/ring-codec "1.0.1"]
 overrides
[duct/module.web "0.7.0"] -> [ring/ring-devel "1.7.1"] -> [ring/ring-core "1.7.1"] -> [ring/ring-codec "1.1.1"]
 and
[duct/module.web "0.7.0"] -> [ring/ring-core "1.7.1"] -> [ring/ring-codec "1.1.1"]
 and
[duct/module.web "0.7.0"] -> [compojure "1.6.1"] -> [ring/ring-codec "1.1.0"]

Consider using these exclusions:
[duct/module.web "0.7.0" :exclusions [ring/ring-codec]]

[kerodon "0.9.0"] -> [ring/ring-codec "1.0.1"] -> [commons-codec "1.6"]
 overrides
[com.datomic/client-pro "0.8.28"] -> [com.datomic/client "0.8.69"] -> [com.datomic/client-impl-shared "0.8.49"] -> [com.cognitect/transit-clj "0.8.313"] -> [com.cognitect/transit-java "0.8.337"] -> [commons-codec "1.10"]
 and
[kerodon "0.9.0"] -> [peridot "0.5.0"] -> [org.apache.httpcomponents/httpmime "4.5.1" :exclusions [commons-logging]] -> [org.apache.httpcomponents/httpclient "4.5.1"] -> [commons-codec "1.9"]
 and
[com.datomic/client-pro "0.8.28"] -> [com.datomic/client-impl-shared "0.8.49"] -> [com.cognitect/transit-clj "0.8.313"] -> [com.cognitect/transit-java "0.8.337"] -> [commons-codec "1.10"]
 and
[com.datomic/client-pro "0.8.28"] -> [com.datomic/client "0.8.69"] -> [com.datomic/client-impl-shared "0.8.49"] -> [com.cognitect/hmac-authn "0.1.194"] -> [commons-codec "1.10"]
 and
[duct/module.web "0.7.0"] -> [ring/ring-devel "1.7.1"] -> [ring/ring-core "1.7.1"] -> [ring/ring-codec "1.1.1"] -> [commons-codec "1.11"]
 and
[duct/module.web "0.7.0"] -> [metosin/muuntaja "0.6.3"] -> [com.cognitect/transit-clj "0.8.313"] -> [com.cognitect/transit-java "0.8.337"] -> [commons-codec "1.10"]
 and
[com.datomic/client-pro "0.8.28"] -> [com.datomic/client-impl-shared "0.8.49"] -> [com.cognitect/hmac-authn "0.1.194"] -> [commons-codec "1.10"]
 and
[duct/module.web "0.7.0"] -> [ring/ring-core "1.7.1"] -> [ring/ring-codec "1.1.1"] -> [commons-codec "1.11"]
 and
[duct/module.web "0.7.0"] -> [compojure "1.6.1"] -> [ring/ring-codec "1.1.0"] -> [commons-codec "1.10"]

Consider using these exclusions:
[com.datomic/client-pro "0.8.28" :exclusions [commons-codec]]
[kerodon "0.9.0" :exclusions [commons-codec]]
[duct/module.web "0.7.0" :exclusions [commons-codec]]

[duct/module.web "0.7.0"] -> [duct/server.http.jetty "0.2.0"] -> [ring/ring-jetty-adapter "1.6.2"] -> [org.eclipse.jetty/jetty-server "9.2.21.v20170120"] -> [org.eclipse.jetty/jetty-io "9.2.21.v20170120"]
 overrides
[com.datomic/client-pro "0.8.28"] -> [com.datomic/client "0.8.69"] -> [com.datomic/client-impl-shared "0.8.49"] -> [com.cognitect/http-client "0.1.87"] -> [org.eclipse.jetty/jetty-client "9.3.7.v20160115"] -> [org.eclipse.jetty/jetty-io "9.3.7.v20160115"]
 and
[com.datomic/client-pro "0.8.28"] -> [com.datomic/client-impl-shared "0.8.49"] -> [com.cognitect/http-client "0.1.87"] -> [org.eclipse.jetty/jetty-client "9.3.7.v20160115"] -> [org.eclipse.jetty/jetty-io "9.3.7.v20160115"]
 and
[com.datomic/client-pro "0.8.28"] -> [com.datomic/client "0.8.69"] -> [com.cognitect/http-client "0.1.87"] -> [org.eclipse.jetty/jetty-client "9.3.7.v20160115"] -> [org.eclipse.jetty/jetty-io "9.3.7.v20160115"]

Consider using these exclusions:
[com.datomic/client-pro "0.8.28" :exclusions [org.eclipse.jetty/jetty-io]]

 [clojure-complete "0.2.5" :exclusions [[org.clojure/clojure]]]
 [com.datomic/client-pro "0.8.28"]
   [com.cognitect/anomalies "0.1.12"]
   [com.datomic/client-impl-shared "0.8.49"]
     [com.cognitect/hmac-authn "0.1.194"]
   [com.datomic/client "0.8.69"]
     [com.cognitect/http-client "0.1.87"]
       [org.eclipse.jetty/jetty-client "9.3.7.v20160115"]
       [org.eclipse.jetty/jetty-http "9.3.7.v20160115"]
       [org.eclipse.jetty/jetty-util "9.3.7.v20160115"]
     [com.datomic/client-api "0.8.13"]
   [org.clojure/core.async "0.3.442"]
     [org.clojure/tools.analyzer.jvm "0.7.0"]
       [org.clojure/core.memoize "0.5.9"]
         [org.clojure/core.cache "0.6.5"]
           [org.clojure/data.priority-map "0.0.7"]
       [org.clojure/tools.analyzer "0.6.9"]
       [org.clojure/tools.reader "1.0.0-beta4"]
       [org.ow2.asm/asm-all "4.2"]
 [duct/core "0.7.0"]
   [fipp "0.6.14"]
     [org.clojure/core.rrb-vector "0.0.13"]
   [integrant "0.7.0"]
     [weavejester/dependency "0.2.1"]
   [medley "1.0.0"]
 [duct/module.logging "0.4.0"]
   [duct/logger.timbre "0.4.1"]
     [com.taoensso/timbre "4.10.0"]
       [com.taoensso/encore "2.91.0"]
         [com.taoensso/truss "1.5.0"]
   [duct/logger "0.3.0"]
 [duct/module.web "0.7.0"]
   [compojure "1.6.1"]
     [clout "2.2.1"]
       [instaparse "1.4.8" :exclusions [[org.clojure/clojure]]]
     [org.clojure/tools.macro "0.1.5"]
   [duct/server.http.jetty "0.2.0"]
     [ring/ring-jetty-adapter "1.6.2"]
       [org.eclipse.jetty/jetty-server "9.2.21.v20170120"]
         [org.eclipse.jetty/jetty-io "9.2.21.v20170120"]
       [ring/ring-servlet "1.6.2"]
   [metosin/muuntaja "0.6.3"]
     [com.cognitect/transit-clj "0.8.313"]
       [com.cognitect/transit-java "0.8.337"]
         [javax.xml.bind/jaxb-api "2.3.0"]
         [org.msgpack/msgpack "0.6.12"]
           [com.googlecode.json-simple/json-simple "1.1.1" :exclusions [[junit]]]
           [org.javassist/javassist "3.18.1-GA"]
     [metosin/jsonista "0.2.2"]
       [com.fasterxml.jackson.core/jackson-databind "2.9.7"]
         [com.fasterxml.jackson.core/jackson-annotations "2.9.0"]
         [com.fasterxml.jackson.core/jackson-core "2.9.7"]
       [com.fasterxml.jackson.datatype/jackson-datatype-jsr310 "2.9.7"]
       [org.ow2.asm/asm "5.1"]
       [virgil "0.1.6"]
   [org.slf4j/slf4j-nop "1.7.25"]
     [org.slf4j/slf4j-api "1.7.25"]
   [org.webjars/normalize.css "5.0.0"]
   [ring-webjars "0.2.0"]
     [org.webjars/webjars-locator "0.27"]
       [org.apache.commons/commons-lang3 "3.4"]
       [org.webjars/webjars-locator-core "0.27"]
         [org.apache.commons/commons-compress "1.9"]
   [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-defaults "0.3.2"]
     [javax.servlet/javax.servlet-api "3.1.0"]
     [ring/ring-anti-forgery "1.3.0"]
     [ring/ring-headers "0.3.0"]
     [ring/ring-ssl "0.3.0"]
   [ring/ring-devel "1.7.1"]
     [clj-stacktrace "0.2.8"]
     [hiccup "1.0.5"]
     [ns-tracker "0.3.1"]
       [org.clojure/java.classpath "0.2.3"]
 [eftest "0.5.7"]
   [io.aviso/pretty "0.1.34"]
   [mvxcvi/puget "1.0.3"]
     [mvxcvi/arrangement "1.1.1"]
   [progrock "0.1.2"]
 [integrant/repl "0.3.1"]
   [org.clojure/tools.namespace "0.2.11"]
 [kerodon "0.9.0"]
   [enlive "1.1.6" :exclusions [[org.clojure/clojure]]]
     [org.ccil.cowan.tagsoup/tagsoup "1.2.1"]
     [org.jsoup/jsoup "1.7.2"]
   [org.flatland/ordered "1.5.4"]
     [org.flatland/useful "0.9.0"]
   [peridot "0.5.0"]
     [org.apache.httpcomponents/httpcore "4.4.5"]
     [org.apache.httpcomponents/httpmime "4.5.1" :exclusions [[commons-logging]]]
       [org.apache.httpcomponents/httpclient "4.5.1"]
     [org.clojure/data.codec "0.1.0"]
     [ring-mock "0.1.5"]
   [ring/ring-codec "1.0.1"]
     [commons-codec "1.6"]
 [nrepl "0.6.0" :exclusions [[org.clojure/clojure]]]
 [org.clojure/clojure "1.10.0"]
   [org.clojure/core.specs.alpha "0.2.44"]
   [org.clojure/spec.alpha "0.2.176"]

I’d love some input!

  • Specifically, how can I unbreak my small example?
  • Generally, recommendations for working with dependency conflicts in Clojure?

Thanks!

Teodor

#2

Small update:

  • Putting in all the recommended :exclusions from lein deps :tree didn’t help
  • There’s a related discussion on Jetty dependency conflicts on the Datomic Forum:
#3

Bigger update!

Rubber duck debugging really helps, guys.

  1. I noticed that multiple org.eclipse.jetty/ packages were named according to the same scheme, for example "9.2.21.v20170120"
  2. Duct required [org.eclipse.jetty/jetty-server "9.2.21.v20170120"], and Datomic required [org.eclipse.jetty/jetty-client "9.3.7.v20160115"]
  3. Together that broke!

Checking my original dependency tree, this lead to (excerpt from lein deps :tree):

;; ... lots of deps ...
;; Datomic deps
   [com.datomic/client "0.8.69"]
     [com.cognitect/http-client "0.1.87"]
       [org.eclipse.jetty/jetty-client "9.3.7.v20160115"]
       [org.eclipse.jetty/jetty-http "9.3.7.v20160115"]
       [org.eclipse.jetty/jetty-util "9.3.7.v20160115"]
     [com.datomic/client-api "0.8.13"]
;; ... lots of more deps ...
;; Duct deps
   [duct/server.http.jetty "0.2.0"]
     [ring/ring-jetty-adapter "1.6.2"]
       [org.eclipse.jetty/jetty-server "9.2.21.v20170120"]
         [org.eclipse.jetty/jetty-io "9.2.21.v20170120"]

… where org.eclipse.jetty/jetty-server depended on jetty-util. However, jetty-util was provided as a dependency of com.cognitect/http-client! Which munched together different Jetty versions.

I decided that I should probably stick to one version. Things magically started working when I manually set ring versions:

;; I added this to :dependencies in project.clj
                 [ring "1.7.1"]
                 [ring/ring-core "1.7.1"]
                 [ring/ring-devel "1.7.1"]
                 [ring/ring-jetty-adapter "1.7.1"]
                 [ring/ring-servlet "1.7.1"]

Fixing Jetty versions instead of Ring versions also worked.

;; alternatively, setting these dependencies also worked:
                 [org.eclipse.jetty/jetty-server "9.4.12.v20180830"]
                 [org.eclipse.jetty/jetty-http "9.4.12.v20180830"]
                 [org.eclipse.jetty/jetty-util "9.4.12.v20180830"]
                 [org.eclipse.jetty/jetty-io "9.4.12.v20180830"]

Lessons learned

  • Working with dependency conflicts isn’t black magic :sunglasses:
  • Problems arise when two libraries that you depend on both require the same library of different versions
  • Leiningen is going to pick the “most specific” version. If you depend directly on a library, that library is picked. If you depend on a lib that depends on a lib that depends on a lib …, that recursive dependency might be deprioritized.
  • Some libraries are split into separate packages, but should be kept together. Use one Ring version. Use one Jetty version.
  • lein deps :tree tells you dependency conflicts in the beginning. Afterwards, you’ll see the resolved versions of all the libraries you’re using. Use this to your advantage! See what libraries are getting a version they’re not expecting. If the library in conflict is developed in a backwards-compatible way, you should be able to use the latest version.
  • As an application author you’re responsible for resolving upstream dependency conflicts.

/monologue done. Hope this might help someone else in the same pit of trouble!

Teodor

3 Likes