Java(Jar) で大量クラスを取り扱う時の zip64 について
あるプロジェクトのビルドをしようとしたところ下記のようなエラーがでてきた。 このプロジェクトは gradle のビルドパフォーマンスを調べるために 10 万クラスを適当に作ったプロジェクトである。
Execution failed for task ':jar'. > archive contains more than 65535 entries.
jar タスクを実行しようとしたとき jar に含められるエントリー数が 65535 を超えているとビルドできないらしい。
軽く検索してみると以下のようなページが出てくる。
java - What is the maximum number of files per jar? - Stack Overflow
この制限は jar アーカイブの話であり、java7 以上であれば zip64 format を使うと一つの jar に 65535 以上のファイルをつかえるらしい。
以下気になったので調べたことをまとめる。
ZIP64 とは
wikipedia を見ると zip64 は zip であり、その拡張領域に zip64 であること、または zip64 の情報が含まれているように見える。 また最近の環境であれば大体サポート対象のため普通に動くようにみえる。
zip の仕様 の version としては 4.5 以上。
https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
多分 fat jar とは関係ないが zipinfo コマンドなどでファイルの中身を見ると fat と書かれている、File Allocation Table の略が出てくる。
File Allocation Table - Wikipedia
Java における zip64
Java SE 7 以上ならばサポートされている様子。
BTS は以下のものの様子。
Bug ID: JDK-4681995 Add support for large (> 4GB) zip/jar files
javadoc をみると zip64 サポートはオプションであり、実装によりそう。
例として adopt openjdk を見るとサポートされている様子。今はもうだいたい Java SE 8 以上だと思うんで 1.6 この辺りは深堀しない。
https://github.com/AdoptOpenJDK/openjdk-jdk/commit/21aa30606a26191e0418ab9fdfd04d9ecfe155a0
Fat Jar における zip64
with Gradle
gradle で build するときには zip64 であるかどうかのオプションを指定することで zip64 形式で jar を生成できる様子。このときの実装は apache ant の ZipOutputStream
。
gradle plugin など gradle 側で利用する側の jar が zip64 形式のとき動くかどうかはドキュメントやコードを見ただけではよくわからなかった。
gradle 1.12 以上ならば サポートされている様子。
Jar - Gradle DSL Version 6.7.1
ZipOutputStream (Apache Ant API)
適当に Web 検索すると gradle buildscript に zip64 オプションを突然入れている記事が見えるので世の中ではよくある要件っぽくみえる。
with Spring Boot
Spring Boot では fully executable jar として jar ファイルを同梱して利用している様子。 この jar ファイルは Spring Boot 側の機能で実現されているようであるため Spring Boot の Loader 自前で頑張って zip64 format を解釈してるように見える。
このサポートは Spring Boot 2.2 から。ただしサポートされているのは同梱するライブラリのほうであり、 Spring Boot Gradle Plugin などで作られる実行可能な jar ファイルについてはサポートされていない。
Spring Boot 2.2 Release Notes · spring-projects/spring-boot Wiki · GitHub
Zip64 files are now supported inside "Fat Jars".
Support zip64 jars by cvienot · Pull Request #16091 · spring-projects/spring-boot · GitHub
Deploying Spring Boot Applications
A zip64-format jar file cannot be made fully executable. Attempting to do so will result in a jar file that is reported as corrupt when executed directly or with java -jar. A standard-format jar file that contains one or more zip64-format nested jars can be fully executable.
このあたりの事情を鑑みるに、Spring Boot Application を実装するプロジェクト上では 65535 以上のファイルを含んだ実行可能ファイルを作ることができない。 そのため一部のコードなどを別のプロジェクトとしてビルドし、それを依存するような構成などにするようなワークアラウンドが必要そう。
with other fat/uber jar
gradle shadow plugin や maven shade plugin などで生成される jar は一回 unzip してその中のクラスファイル等を追加して zip しなおす。 このときの jar はおそらく gradle の jar task に依存するため zip64 option を設定することで zip64 形式の jar を吐き出すことができる様子。