非モジュールJavaFXのアプリをJlinkで配布可能パッケージにする
このエントリーはJavaFX Advent Calendarの24日目です。
qiita.com
Java SE 9からjlinkコマンドが使えるようになり、カスタムJREを作れるようになりました。
ところが、jlinkを使うにはモジュールアプリケーションでないとダメだというような間違った言説が流れているような気がします。
jlinkで作るカスタムJREは非モジュールアプリケーションでも可能なんです!!!!
ということで、JavaFXの非モジュールアプリでカスタムJREを作ってみましょう。
サンプルは超簡単なラベルを表示するだけのものです。
package net.javainthebox.hello; import javafx.application.Application; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.stage.Stage; public class Hello extends Application { @Override public void start(Stage stage) throws Exception { var label = new Label("Hello, JavaFX!"); label.setAlignment(Pos.CENTER); var scene = new Scene(label, 200, 50); stage.setScene(scene); stage.show(); } public static void main(String... args) { launch(args); } }
これをMavenでビルドするのは、以下のエントリーでも書いた通り(pom.xmlに記述した各種バージョンは新しいものにしてくださいね)。
もちろん、自分でビルドするのでも全然かまいません。
ここでは、C:\hello\srcにソースファイルがあるとします。また、JavaFX SDKは"C:\Program Files\Java\javafx-sdk-11.0.1"に配置してあります。
C:\hello\src>javac --module-path "C:\Program Files\Java\javafx-sdk-11.0.1\lib" --add-modules javafx.controls -d ..\bin net\javainthebox\hello\Hello.java C:\hello\src>cd .. C:\hello>jar --create --file hello.jar -C bin .
これで、helloディレクトリ直下にhello.jarができました。試しに実行してみましょう。
C:\hello>java --module-path "C:\Program Files\Java\javafx-sdk-11.0.1\lib" --add-modules javafx.controls -cp hello.jar net.javainthebox.hello.Hello
これでラベルだけのフレームが表示されるはずです。
カスタムJREを作成する
jlinkを使う前にjdepsを使わなくてはいけないような風潮がありますが、使っているモジュール(ここではJavaFXのモジュール)が明らかであれば、jdepsを使う必要はありません。
また、非モジュールアプリケーションの場合、jdepsの--print-module-depsや--list-depsオプションは使用できません(JDKのモジュールしか表示されません)。
もし、jdepsを使うのであれば、オプションなしに使用します(もしくはサマリー表示の-s)。
C:\hello>jdeps --module-path "C:\Program Files\Java\javafx-sdk-11.0.1\lib" -s hello.jar hello.jar hello.jar -> java.base hello.jar -> javafx.controls hello.jar -> javafx.graphics javafx.base -> java.base javafx.base -> java.desktop javafx.controls -> java.base javafx.controls -> javafx.base javafx.controls -> javafx.graphics javafx.fxml -> java.base javafx.fxml -> java.scripting javafx.fxml -> java.xml javafx.fxml -> javafx.base javafx.fxml -> javafx.graphics javafx.graphics -> java.base javafx.graphics -> java.desktop javafx.graphics -> java.xml javafx.graphics -> javafx.base javafx.graphics -> jdk.unsupported javafx.media -> JDK removed internal API javafx.media -> java.base javafx.media -> javafx.base javafx.media -> javafx.graphics javafx.swing -> java.base javafx.swing -> java.datatransfer javafx.swing -> java.desktop javafx.swing -> javafx.base javafx.swing -> javafx.graphics javafx.swing -> jdk.unsupported.desktop javafx.swt -> java.base javafx.swt -> javafx.base javafx.swt -> javafx.graphics javafx.swt -> 見つかりません javafx.web -> java.base javafx.web -> java.desktop javafx.web -> java.xml javafx.web -> javafx.base javafx.web -> javafx.controls javafx.web -> javafx.graphics javafx.web -> javafx.media javafx.web -> jdk.jsobject javafx.web -> jdk.xml.dom
ずらずらと出てきますが、重要なのは上の方の3行。hello.jarが直接使用しているのはjava.base、javafx.controls、javafx.graphicsモジュールの3つだということ。javafx.graphicsモジュールはjavafx.controlsモジュールが依存しているので、実質はjava.baseとjavafx.controlsだけでOKです。
これが分かればjlinkでJREが作れます。というか、javacやjavaで実行するときに--add-modulesでjavafx.controlsを加えているので、jdepsなど使わなくても何を使っているかは自明ですけどね。
なお、jlinkでJREを作る時、JavaFX SDKのJARファイルよりJMODファイルを使う方がおススメです。JARでもいいのですが、JARファイルにはネイティブライブラリ(DLLやSO)が含まれていないので、別途コピーする必要があります。
JMODファイルであればネイティブライブラリも含んでいるので、簡単です。ここではJMODファイルは"C:\Program Files\Java\javafx-sdk-11.0.1\mods"に置いてあるとします。
C:\hello>jlink --module-path "C:\Program Files\Java\javafx-sdk-11.0.1\mods" --add-modules java.base,javafx.controls --compress=2 --output jre
お分かりだろうとは思いますが、このJREにはアプリケーションのJARファイルは含まれていません。カスタムJREに含められるのはあくまでもモジュールだけです。この場合は標準のモジュールとJavaFXのモジュールで構成されています。
他にモジュールで提供されているライブラリを使用しているのであれば、それもJREに含めることができます。
逆にいうと、非モジュールのライブラリやアプリケーションJARはJREには含まれていないので、実行にはクラスパスで指定する必要があります。
では、実行してみましょう。ここでは、クラスパスでhello.jarを指定しています。
C:\hello>jre\bin\java -cp hello.jar net.javainthebox.hello.Hello
JREのモジュールにjavafx.controlsなどが含まれているので、--module-pathや--add-modulesなどのオプションは必要ありません。
では、配布可能にするためにjreディレクトリにhello.jarもコピーしておきましょう。
C:\hello>copy hello.jar jre 1 個のファイルをコピーしました。
後は、実行に便利なようにバッチファイル、もしくはシェルスクリプトを作っておきましょう。たとえば、hello.batを作ったとします。ファイルの内容は以下の1行だけ。
bin\java -cp hello.jar net.javainthebox.hello.Hello
試しに、hello.batで正しく実行できるかどうか試しておいてください。
これで、jreディレクトリをそのまま配布することができます(ZIPファイルにするとか、ディレクトリ名を変えるとかはご自由に)。