日々常々

ふつうのプログラマがあたりまえにしたいこと。

Apache POIのバージョンあげたらサイズ跳ね上がったので調べたログ

Apache POI の久々のメジャーバージョンアップである 5.0.02021-01-20 に出ました。 テストも通るしいっかーと、雑に更新したらexecutable jarのサイズが26MB増えてウケました。(笑いごとじゃない

多少のJarサイズはそれほど問題にならないことが多いのですが、今回はもともと40MB程度のJarで、それでも「ちょっと大きいなー」って思っていたのがいきなり60MBを超えただけに、ちょっと待ってくれって気分です。

そんなわけでちょっとみてみようかーと。見ていった手順をだらだら書きます。参考になるかどうかは知りません。

ちなみにPOIのソース管理は Subversion で、GitHubにあるのはミラーです。 今回ソースは読んでないけど。ライブラリのソースは本体のリポジトリじゃなく、Mavenリポジトリ-sources.jar で読むことの方が多いと思います。でも本体の場所を知ってると、ごく稀にコミットログとか追いたくなったときに便利です。

空のjarを作るプロジェクトを作る

調べるのに他のライブラリが混ざってるとかったるいので、ピュアなプロジェクトを作ります。 ビルドにいちいち時間かかるのも嫌だし。

雑にcatbuild.gradle を書きます。

% cat>build.gradle
plugins { id 'java' }
^C

一つもクラスを作らずにビルドします。

% gradle build

BUILD SUCCESSFUL in 537ms
1 actionable task: 1 up-to-date

できました。 中身は もはや人間が実行することもなくなった jar コマンドでみれます。

% ls -l build/libs
total 8
-rw-r--r--  1 irof  staff  261  2  3 22:43 poi5.jar
% jar -tf build/libs/poi5.jar
META-INF/
META-INF/MANIFEST.MF

MANIFEST.MF だけですね。この手順は「Gradleが動いてる」とかその辺の確認です。すぐ確認できるんだからすぐ確認する。あ、ディレクトリ名を poi5にしたんでその名前になってますね。

jar コマンドがよくわからないなら拡張子を .zip に変えて適当に開いてやればよいです。昔はよくやってました。jarってzipなんで。

poiを入れる

引き続きcatで追記します。

% cat>>build.gradle
repositories.jcenter()
dependencies.implementation 'org.apache.poi:poi:5.0.0', 'org.apache.poi:poi-ooxml:5.0.0'
^C

repositoriesmavenCentralでもいいです。Gradleの次のアップデート以降で非推奨になったりするんだろな。

追記: てか jcenter なくなります。タイムリーすぎてウケた(笑い事じゃない

implementationとかは実務では1artifactずつ並べるんですが、今回は cat で書いてるから滅多に使わない複数列挙をします。 repositoriesとかdependencies とかは普通 {} で書きますが、一つだったらそのまま . で繋げられます。滅多に役に立たない知識。 この書き方は実務ではお勧めしません。

ooxmlとかは ブログの過去ログを検索すれば……10年以上前か。当時使ってたのが3.5なので、長いこと中身読んでないなぁ。。

build.gradle全体

plugins { id 'java' }
repositories.jcenter()
dependencies.implementation 'org.apache.poi:poi:5.0.0', 'org.apache.poi:poi-ooxml:5.0.0'

しんぷる。

依存ツリーを確認

gradle dependencies でみれます。ようするにMavendependency:tree ……なんだけど、IDE上でGUIで見ることの方が多いかなと思います。コマンド知らなくても特に問題ないんじゃないかな。

--configuration 無しだと compileClasspath とか testRuntimeClasspath とか色々でてノイズなので、眺めるときはだいたい runtimeClasspath を使ってます。

% gradle dependencies --configuration runtimeClasspath

> Task :dependencies

------------------------------------------------------------
Root project 'poi5'
------------------------------------------------------------

runtimeClasspath - Runtime classpath of source set 'main'.
+--- org.apache.poi:poi:5.0.0
|    +--- org.slf4j:slf4j-api:1.7.30
|    +--- org.slf4j:jcl-over-slf4j:1.7.30
|    +--- commons-codec:commons-codec:1.15
|    +--- org.apache.commons:commons-collections4:4.4
|    +--- org.apache.commons:commons-math3:3.6.1
|    \--- com.zaxxer:SparseBitSet:1.2
\--- org.apache.poi:poi-ooxml:5.0.0
     +--- org.apache.poi:poi:5.0.0 (*)
     +--- org.apache.poi:poi-ooxml-lite:5.0.0
     |    \--- org.apache.xmlbeans:xmlbeans:4.0.0
     +--- org.apache.commons:commons-compress:1.20
     +--- com.github.virtuald:curvesapi:1.06
     +--- org.bouncycastle:bcpkix-jdk15on:1.68
     |    \--- org.bouncycastle:bcprov-jdk15on:1.68
     +--- org.bouncycastle:bcprov-jdk15on:1.68
     +--- org.apache.santuario:xmlsec:2.2.1
... 略 ...
     +--- org.apache.xmlgraphics:batik-all:1.13
... 略 ...
     \--- de.rototor.pdfbox:graphics2d:0.30
          \--- org.apache.pdfbox:pdfbox:2.0.22
               \--- org.apache.pdfbox:fontbox:2.0.22

削ってるからあれだけど、めっちゃいっぱい出てました。poi-ooxml由来で189個。

詳細は省略しますが、org.apache.xmlgraphics:batik-all で約13MB、 org.bouncycastle で約7MB、 de.rototor.pdfbox:graphics2d で約5MB。これらで約23MBになります。増加分とほぼ合いますね。

Apache BatikSVGを扱うもの。Bouncy Castleは暗号化。最後のはApache PDFBoxのアダプタなんでPDFを扱うものです。 なるほどこれらに対応したのかーと言う感じですね。POI使う時に制約になった記憶がうっすらあります。うっすら。(でも今回はシンプルな xlsx 扱うだけなんで使わない……)

リリースノートを確認

最初にやれ←

なるほど(わからん

ちなみに poi-ooxml-lite とかがあって、これ使うといいんじゃ?と思いましたが、こちらは元 ooxml-schemas と書いてるようにスキーマだけ。使いたい XSSFWorkbook とかは入っておらずでした。

ちなみに前バージョン

% gradle dependencies --configuration runtimeClasspath

> Task :dependencies

------------------------------------------------------------
Root project 'poi5'
------------------------------------------------------------

runtimeClasspath - Runtime classpath of source set 'main'.
+--- org.apache.poi:poi:4.1.2
|    +--- commons-codec:commons-codec:1.13
|    +--- org.apache.commons:commons-collections4:4.4
|    +--- org.apache.commons:commons-math3:3.6.1
|    \--- com.zaxxer:SparseBitSet:1.2
\--- org.apache.poi:poi-ooxml:4.1.2
     +--- org.apache.poi:poi:4.1.2 (*)
     +--- org.apache.poi:poi-ooxml-schemas:4.1.2
     |    \--- org.apache.xmlbeans:xmlbeans:3.1.0
     +--- org.apache.commons:commons-compress:1.19
     \--- com.github.virtuald:curvesapi:1.06

こっちは省略なしです。シンプルなものですね。

上でサイズの大半を占めてるって書いてたライブラリはこっちでも使ってないし exclude しちゃっていい気がする。 ってことで外して、テスト通って、OK。と言う感じ。

注意と昔話

Mavencompile スコープで宣言されるものを外すのは危険です。自信とそれを裏付ける自動テストがなければやめましょう。(とか言いながら自信はなかったりする。) ライブラリの依存ライブラリがないと、実行時にクラスねーよって落ち方します。コンパイルチェックが効かない。せっかくコンパイル言語使ってるのにこれが起こるのはダサい。

Maven以前は自身でJarを各ライブラリの配布サイトからかき集めてクラスパスに追加する作業が必要で、この時に集めるのが漏れて実行時エラーとかは結構よくある話でした。 でも現代ではこの苦労をする必要はありません。それがMavenが変えたJavaの世界です。

自動で入る依存ライブラリを外すのは、解決済みの問題のトリガーを自身で引く行為です。 必要な場合に、ある程度(完全ではない)安全に行うには自動テストが必須です。完全じゃないってのは肝に銘じた上で。