JDK9のモジュールとjlinkでアプリ配布向けのJVMを作る

  • 9
    Like
  • 0
    Comment

JavaOne2017を前にして、待望のJava9が遂にリリースしましたね!

さて、Java9といえばやはり気になるのはjigsawにるモジュール機能です。モジュールの使い方までは良く見ますが、jlinkが個人的には気になってたので試した結果をまとめました。

はじめに

Jigsawに関してですが少なくとも現時点では、fat-jarやgo言語のようなシングルバイナリを代替するようなことは単独ではできません。

ただ、モジュールとjlinkを使うことでアプリケーションを含んだ配布用のJVMを生成することが可能で、今回はそれについての説明になります。
モジュールで公開範囲の改善や依存の早期発見ができるようになったことは特に触れないので、その辺はこの記事とかを参考にされると良いと思います。

コードの準備

まずは、コードの準備です。
下記のような感じでアプリからライブラリが三階層で呼ばれてるようなサンプルを作ります。

alt

こちらにできたものが。特に語ることがないので詳細は省きますが、それぞれのトップディレクトリにmodule-info.javaを配置して、モジュールの設定を書いています。
https://github.com/koduki/example-jlink

ビルド

続いて各モジュールのビルドです。

その前にまずモジュール配置先のディレクトリを作ります。

$ mkdir modules

当たり前ですが、依存が無い方から順番にビルドしていきます。

$ javac -d commons/classes/ -cp commons/src/main/java $(find commons/src/main/java -name "*.java")
$ jar cvf modules/commons.jar -C commons/classes/ .

ビルドは基本的には通常のjarと同じで良さそうですが、コンパイル時にpオプションでモジュールのディレクトリを指定するところが違います。
また、古いバージョンだとmpとしてオプションが記載されているので要注意です。

$ javac -d libs/classes/ -cp "libs/src/main/java/" -p modules/ $(find libs/src/main/java -name "*.java") 
$ jar cvf modules/libs.jar -C libs/classes/ .

ライブラリがビルドできたらメインアプリケーション。とはいえこれも他と同様です。

$ javac -d apps/classes/ -cp apps/src/ -p modules $(find apps/src -name "*.java")
$ jar cvf modules/app.jar -C apps/classes/ .

以下のような形で実行できます。

$ java -p modules/ -m apps/cn.orz.pascal.app.MyApp
Hello, cn.orz.pascal.common.CommonLib
Hello, cn.orz.pascal.lib.MyLib
Hello, MyApp

実行が確認できましたね? モジュールを指定するときはmオプションでモジュール名:実行するクラスという形で記述します。

JLinkによる配布用JVMの作成

これだけだと、配布観点ではあまり嬉しく無いので、いよいよJLinkの出番です。
JLinkを使えば指定したモジュールに必要な最小構成のJVMが作れます。つまり、WebアプリでSwingやAWTのライブラリを持たなくて良くなるという事です。

Macの場合はインストールしただけだとパスが通って無いので下記のように対応します。

$ export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/   
$ alias jlink=${JAVA_HOME}/bin/jlink     

続いてjlinkによるリンクの実行です。今回は合わせてapps/cn.orz.pascal.app.MyAppを実行するランチャーの作成と、全体のzipでの圧縮をしています。

$ jlink --compress=2 --module-path $JAVA_HOME/jmods:modules --add-modules apps --output dist/my-app --launcher myapp=apps/cn.orz.pascal.app.MyApp 

実行するとdistディレクトリにmy-appができています。実行すると以下の通り。

$ ./dist/my-app/bin/myapp 
Hello, cn.orz.pascal.common.CommonLib
Hello, cn.orz.pascal.lib.MyLib
Hello, MyApp

my-appには指定したモジュールと最小構成のJVMが入ってるのでこのディレクトリを配布するだけで、JVMが無い環境でもそのまま動きます。ポータブルですね!

なお、容量は今回だと下記の通り。わりといい感じです。

$ du -h dist|tail -n1
 24M    dist

まとめ

jlinkによるアプリ配布向けのJVMの作り方について試してみました。パッケージングこそされないものの、JVMがインストールされてない環境への配布もずいぶん楽そうです。
サーバ用途ならDockerにパッケージさせてしまえば特に困らないんじゃ無いでしょうか?

コマンド系など、go言語やjavaのfat-jarで実現してたようなシングルバイナリでの配布には向きませんが、今後electron-packagerみたいなのが作られれば、十分実現できると思います。

それでは、Happy Hacking!