概要
このエントリでは、Gradleを使っているときに、依存性解決がなんだかうまくいかない時の一例、について紹介します。
Enterprise電卓を作る Advent Calendar 2019の12/3のエントリです。
想定読者
-
Gradleを使っていて、SpringBootを使っているときに、依存性解決において、サードパーティーのライブラリがなんだか古いバージョンが使用されるんだが、という経験がある方。
-
JLine3をSpringBootで使うとき、WindowsプラットフォームでJNAで起動しようとしているときに困っている方。
事象
ログ
Javaでコンソールでのキー入力を1文字ずつハンドルしたいときにJLineを使うのエントリを書いていた際、下記のようなエラーが出ました。こういった事象に対応するための手段の一つを紹介します。
このログを見るに、
Suppressed: java.lang.NoSuchMethodError: com.sun.jna.Native.load(Ljava/lang/String;Ljava/lang/Class;Ljava/util/Map;)Lcom/sun/jna/Library;
ということで、利用しているライブラリの中で、特定のクラスにメソッドがないんだけど、という風に読めます。
Gradleを使って依存性解決をお任せしているのになんじゃこれ、ということになりますが、そういうことになっているので仕方がありません。
確認
Gradleで、プロジェクトが利用しているライブラリの確認コマンドは、下記です。
gradle dependencies
例えば、下図のようなシンプルなbuild.gradleを用意して、
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation group: 'org.jline', name: 'jline', version: '3.12.1'
implementation group: 'org.jline', name: 'jline-terminal-jna', version: '3.12.1'
}
実行すると、下図のようなツリーが表示されます。それぞれのタスクにおいて使用されるライブラリがわかります。
> Task :dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
annotationProcessor - Annotation processors and their dependencies for source set 'main'.
No dependencies
apiElements - API elements for main. (n)
No dependencies
archives - Configuration for archive artifacts.
No dependencies
compile - Dependencies for source set 'main' (deprecated, use 'implementation' instead).
No dependencies
compileClasspath - Compile classpath for source set 'main'.
+--- org.jline:jline:3.12.1
\--- org.jline:jline-terminal-jna:3.12.1
+--- net.java.dev.jna:jna:5.3.1
\--- org.jline:jline-terminal:3.12.1
compileOnly - Compile only dependencies for source set 'main'.
No dependencies
default - Configuration for default artifacts.
+--- org.jline:jline:3.12.1
\--- org.jline:jline-terminal-jna:3.12.1
+--- net.java.dev.jna:jna:5.3.1
\--- org.jline:jline-terminal:3.12.1
implementation - Implementation only dependencies for source set 'main'. (n)
+--- org.jline:jline:3.12.1 (n)
\--- org.jline:jline-terminal-jna:3.12.1 (n)
runtime - Runtime dependencies for source set 'main' (deprecated, use 'runtimeOnly' instead).
No dependencies
runtimeClasspath - Runtime classpath of source set 'main'.
+--- org.jline:jline:3.12.1
\--- org.jline:jline-terminal-jna:3.12.1
+--- net.java.dev.jna:jna:5.3.1
\--- org.jline:jline-terminal:3.12.1
runtimeElements - Elements of runtime for main. (n)
No dependencies
runtimeOnly - Runtime only dependencies for source set 'main'. (n)
No dependencies
testAnnotationProcessor - Annotation processors and their dependencies for source set 'test'.
No dependencies
testCompile - Dependencies for source set 'test' (deprecated, use 'testImplementation' instead).
No dependencies
testCompileClasspath - Compile classpath for source set 'test'.
+--- org.jline:jline:3.12.1
\--- org.jline:jline-terminal-jna:3.12.1
+--- net.java.dev.jna:jna:5.3.1
\--- org.jline:jline-terminal:3.12.1
testCompileOnly - Compile only dependencies for source set 'test'.
No dependencies
testImplementation - Implementation only dependencies for source set 'test'. (n)
No dependencies
testRuntime - Runtime dependencies for source set 'test' (deprecated, use 'testRuntimeOnly' instead).
No dependencies
testRuntimeClasspath - Runtime classpath of source set 'test'.
+--- org.jline:jline:3.12.1
\--- org.jline:jline-terminal-jna:3.12.1
+--- net.java.dev.jna:jna:5.3.1
\--- org.jline:jline-terminal:3.12.1
testRuntimeOnly - Runtime only dependencies for source set 'test'. (n)
No dependencies
(n) - Not resolved (configuration is not meant to be resolved)
A web-based, searchable dependency report is available by adding the --scan option.
BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed
これを見ると、org.jline:jline-terminal-jna:3.12.1がnet.java.dev.jna:jna:5.3.1に依存しているので、それをGradleが使えるようにしてくれることを示します。
目の前の事象
さて、ここでプラグインを2つ追加してみます。SpringInitializrでSpringBootのプロジェクトを作った時に追加されているものです。
build.gradleの全体は下図のようになります。
plugins {
id 'org.springframework.boot' version '2.2.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation group: 'org.jline', name: 'jline', version: '3.12.1'
implementation group: 'org.jline', name: 'jline-terminal-jna', version: '3.12.1'
}
依存関係を確認してみましょう。
default - Configuration for default artifacts.
+--- org.jline:jline:3.12.1
\--- org.jline:jline-terminal-jna:3.12.1
+--- net.java.dev.jna:jna:5.3.1 -> 4.5.2
\--- org.jline:jline-terminal:3.12.1
runtimeClasspath - Runtime classpath of source set 'main'.
+--- org.jline:jline:3.12.1
\--- org.jline:jline-terminal-jna:3.12.1
+--- net.java.dev.jna:jna:5.3.1 -> 4.5.2
\--- org.jline:jline-terminal:3.12.1
あれ、、、net.java.dev.jna:jna:5.3.1 -> 4.5.2となり、古いバージョンのものを参照してしまっています。
この状況はプラグインの動作のためらしく、Gradleのコミュニティサイトでもやり取りがありました。以下引用。
However, as you already suspected, Spring Gradle plugins do configure the dependency resolution by providing their own dependency management plugin:
https://github.com/spring-gradle-plugins/dependency-management-plugin 84.
Using the spring boot plugin, you need to use that, but you can reconfigure dependencies there
暫定対策
将来はこのあたりもうまくいってくれることを願いつつ、暫定対策で自分でdependencyを一つ追加します。build.gradleは下図のようになります。
plugins {
id 'org.springframework.boot' version '2.2.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation group: 'org.jline', name: 'jline', version: '3.12.1'
implementation group: 'org.jline', name: 'jline-terminal-jna', version: '3.12.1'
implementation group: 'net.java.dev.jna', name: 'jna', version: '5.3.1'
}
これにより、意図したバージョンのライブラリが使われるようになりました。
default - Configuration for default artifacts.
+--- org.jline:jline:3.12.1
+--- org.jline:jline-terminal-jna:3.12.1
| +--- net.java.dev.jna:jna:5.3.1
| \--- org.jline:jline-terminal:3.12.1
\--- net.java.dev.jna:jna:5.3.1
これで冒頭のエラーがなくなりました。
おわりに
このエントリでは、Gradleの依存解決で、うまくいかないときの対処の一例として、依存関係の確認の仕方と、利用ライブラリを上書きでしていするやりかたをご紹介しました。