2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

sbt-assembly で FAT JAR を生成しようとしたら module-info.class の重複で怒られた話

Last updated at Posted at 2022-10-24

FAT JAR(もしくは über JAR)とは、Java アプリケーション本体と依存ライブラリ等を1つの JAR ファイルに「アセンブル」したものです。依存ライブラリを含めてパッケージングされており、単体で実行可能になっています。メインクラスを持つ FAT JAR であれば、以下のように特にクラスパスを設定をしなくても実行できます:

java -jar <jarfile>

-jar オプションで起動したとき、JAR ファイル内に含まれるクラスファイルが唯一のユーザークラス・ソースとなります。FAT JAR には実行時に必要とされる全てのクラスが含まれているため、これで問題ありません。ファイル構成・起動コマンドがシンプルになるので、デプロイ時に便利ですよね1

さて、sbt-assembly は FAT JAR を生成するための sbt プラグインです。Maven Assembly Plugin の sbt 版ですね。ためしに logback-classiclibraryDependencies に追加して sbt assembly すると以下のようなエラーとなります。

[error] (assembly) deduplicate: different file contents found in the following:
[error] /root/.cache/coursier/v1/https/repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.4.4/logback-classic-1.4.4.jar:module-info.class
[error] /root/.cache/coursier/v1/https/repo1.maven.org/maven2/ch/qos/logback/logback-core/1.4.4/logback-core-1.4.4.jar:module-info.class

module-info.class というファイルが重複してエラーになっているようですが、これはなんでしょう?

Java のモジュールシステム(Project Jigsaw)と module-info.class

Java SE 9 以降(LTS としては Java SE 11 以降)で導入された新しい2仕様であり、いわゆるクラスパス地獄だとか JAR 地獄といわれる問題を回避するために提案されました。モジュールシステムについてはこちらの記事に詳しいです:モジュールシステムを学ぶ

module-info.class (ソースとしては module-info.java ) はモジュールの定義情報ファイルです。これがない場合は「自動モジュール」(モジュールパスにある場合。クラスパスの場合は「無名モジュール」)として、以下のように扱われます:

  • すべてのパッケージを exports している
  • モジュールグラフに読み込まれたすべてのモジュールを requires している

なお、Scala / sbt はモジュールシステムをサポートしていないため、成果物は状況に応じて自動モジュールないしは無名モジュールとして認識されることになります。

assemblyMergeStrategy でエラーを解消する

Java SE 8 準拠の環境の場合、モジュールシステムを認識しないので assemblyMergeStrategy を定義して破棄してしまえば問題ありません。

build.sbt
ThisBuild / assemblyMergeStrategy := {
    case PathList(ps @ _*) if ps.last endsWith "module-info.class" =>
        MergeStrategy.discard
    case x =>
        val oldStrategy = (assembly / assemblyMergeStrategy).value
        oldStrategy(x)
}

Java SE 9 以降(LTS としては Java SE 11 以降)の場合でも、FAT JAR 内に必要なクラスがそろっているため、元のライブラリが持っていたモジュール情報は必要ありません。FAT JAR をライブラリとして利用する場合ならともかく、FAT JAR の主な用途となる起動可能な単一ファイルとして利用するケースなら大丈夫そうですね。

参考リンク

  1. とはいいつつ、デプロイを易化するという意味では JDK ごと Docker コンテナに押し込んでしまう方法が普及した感もあります。

  2. Java SE 11 が新しいといっていいものかどうかには諸説あります。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?