How do I do exclusions for dependencies that are p

2020-03-26 04:30发布

问题:

I'm writing a custom plugin for Leiningen that will help in deploying AWS Lambda functions written in Clojure. I'm using aws-java-sdk-lambda version 1.11.145 which has a dependency on Jackson 2.6.x, specifically 2.6.5 IIRC.

Since I am writing this as a plugin, the code is executing in the context of Leiningen (i.e. :eval-in-leiningen true) and, AFAIK, is subject to the dependencies of Leiningen itself.

This is a problem since the version of Leiningen I am running (2.7.1) is dependent on Cheshire 5.5.0 which, in turn, is dependent on Jackson 2.5.3. This results in a NoSuchMethodError when the AWS SDK attempts to call com.fasterxml.jackson.databind.JavaType.isReferenceType(). AFAIK, jackson-core-2.6.5 (pulled in by AWS) is attempting to call into jackson-databind-2.5.3 (pulled in, indirectly, by Cheshire). At least that is what appears to be happening.

First question: is this a known issue? I was not able to find anything directly referencing it on SO, Google or the first page of issues on Leiningen's GitHub page (I did not do an exhaustive search there). Second: Does anyone have any suggestions for working around this problem? :exclusions seems like the obvious choice but I'm not sure how one would specify an exclusion for something that Leiningen itself is pulling in as a dependency.

Below, I have included simple project specifications to reproduce the error:

plugin's project.clj:

(defproject aws-plugin "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 [[com.amazonaws/aws-java-sdk-lambda "1.11.145"]]
  :eval-in-leiningen true)

plugin's source code:

(ns leiningen.aws-plugin
  (:import (com.amazonaws.services.lambda AWSLambdaClient
                                          AWSLambdaClientBuilder)))

(def aws-lambda-client (-> (AWSLambdaClient/builder) (.build)))

(defn aws-plugin
  "I don't do a lot, or anything at all."
  [project &]
  (println "Create Lambda"))

plugin user's project.clj

(defproject aws-plugin-user "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.8.0"]]
  :target-path "target/%s"
  :plugins [[aws-plugin "0.1.0-SNAPSHOT"]]
  :profiles {:uberjar {:aot :all}})

Once this is setup, run lein install from the plugin directory and then run lein aws-plugin from the plugin users directory. This should attempt to create an instance of AWSLambdaClient which will, in turn, trigger the error.

回答1:

First question: is this a known issue?

This is a known issue, and one that many people writing Leiningen plugins face. The root cause is that Leiningen builds an uberjar with all of its dependencies, and those dependencies are not shaded. This means that if there is any collision between Jackson classes, Leiningen's one will always be chosen. What makes this especially devious is that you won't get any warnings about conflicting dependencies, as the JARs don't conflict, but the contents of them do.

Here's a few examples of this problem: https://github.com/s3-wagon-private/s3-wagon-private/issues/38, https://github.com/technomancy/leiningen/issues/2215.

Second: Does anyone have any suggestions for working around this problem?

Issues with Clojure inter-dependency with different major versions of the same library has some suggestions for the general case, although it gets a little trickier when Lein's dependencies are uberjarred.

I think your best bet at this point would be to shade the Jackson dependency and the AWS SDK.

Leiningen master no longer depends on Cheshire though, and when 2.8.0 is released you should have less problems.