JavaFX 8からJavaFX 11以降へのマイグレーション
前回のエントリーでJavaFX 8はやめようという話をしました。
JavaFX 8からJavaFX 11以降のバージョンへの移行は、ほとんどの場合、ビルド/実行方法を変えるだけです。今回はサンプルを使って、それを説明していきます。
今回使用するサンプルは、散布図と一次回帰を表示するというアプリケーションです。表示するデータはCSVで、その読み込みにはOpenCSVを使用しています。また、一次回帰の計算にはApache Commons Mathを使用しました。
ソースはこちら。
JavaFX 8で作成したJARをJavaFX 12で動かす
GitHubのscatterplotterのブランチjavafx8でJavaFX 8用のソースになります。
今回はこれをLiberica JDK 8u212を使ってビルドしました。ビルドにはMavenを使用し、前述したようにOpenCSVとApache Commons Mathを使っているので、依存性に記述しています。
では実行してみましょう。ここでは、Windowsで実行しています。
もちろん、Mavenを使っても実行できるのですが、ここではJava 8とJava 11の違いが分かるように、コマンドラインで実行してみました。
Mavenなので、targetディレクトリにJARファイルが作られます。また、クラスパスで指定できるように、依存ライブラリをtarget/dependencyにコピーしておきます。
C:\scatterplotter> mvn dependency:copy-dependencies
実行は次のように行います。
C:\scatterplotter>java -cp target\scatterplotter-1.0-SNAPSHOT.jar;target\dependency\* net.javainthebox.scatterplotter.Main
2カラムのCSVで、先頭行が軸の名前になっているファイルを読み込むと上のような散布図と一次回帰線が表示されるはずです。
では、これをJavaFX 12.0.1とJava 11.0.3 (ここでは、AdoptOpenJDKのLTSを使用しました)を使って実行してみます。
JavaFX 12.0.1を使うには、以下のURLの"Latest Release"からJavaFX SDKをダウンロードして、適当な場所に展開します。ここではC:\Program Files\Javaに展開しました。
では、実行してみます。
C:\scatterplotter>"C:\Program Files\AdoptOpenJDK\jdk-11.0.3.7-hotspot\bin\java" --module-path "C:\Program Files\Java\javafx-sdk-12.0.1\lib" --add-modules javafx.fxml,javafx.controls -cp target\scatterplotter-1.0-SNAPSHOT.jar;target\dependency\* net.javainthebox.scatterplotter.Main
Java 8の場合と異なるのは、--module-pathオプションと--add-modulesオプションです。--module-pathオプションには先ほど展開したJavaFX SDKのlibディレクトリを指定します。--add-modulesオプションにはJavaFXで使用するモジュールを追加します。
ここでは、javafx.controlsとjavafx.fxmlを指定しています。JavaFXのモジュールとしては、javafx.baseとjavafx.graphicsを使用していますが、javafx.controlsもこの2つのモジュールに依存しているので、--add-modulesオプションに指定する必要はありません。
逆にいうと、このオプションだけを指定すれば、ソースを変更することなくJavaFX 12でも実行することができます。
でも、JavaFX SDKを展開しておかなくてはいけないなど、事前の準備が必要です。そこで、JavaFX 11以降のバージョン用にビルド・実行環境を作成します。
JavaFX 12でのビルド・実行環境を作る
まず、プロジェクトの依存性にJavaFXを加えます。OpenCSVとCommons Mathだけだったところに、javafx-fxmlとjavafx-controlsを加えます。
<dependencies> <dependency> <groupId>com.opencsv</groupId> <artifactId>opencsv</artifactId> <version>4.6</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-math3</artifactId> <version>3.6.1</version> </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-fxml</artifactId> + <version>12.0.1</version> + </dependency> + <dependency> + <groupId>org.openjfx</groupId> + <artifactId>javafx-controls</artifactId> + <version>12.0.1</version> + </dependency> </dependencies>
行頭に+がついている部分が追加した部部分です。
ここでは、javafx-fxmlとjavafx-controlsだけですが、必要に応じて他のモジュールを加えます。たとえば、ムービーやサウンドを使う場合はjavafx-media、WebViewを使いたいのであればjavafx-webを加えます。
前述したように、javafx-controlsはjavafx-graphicsとjavafx-baseに依存しているので、自動的に取り込まれます。
これだけでもいいのですが、MavenのJavaFX Pluginがあるので、これをExec Maven Pluginの代わりに使ってみましょう。
<plugins> <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <version>1.6.0</version> - <executions> - <execution> - <goals> - <goal>java</goal> - </goals> - </execution> - </executions> + <groupId>org.openjfx</groupId> + <artifactId>javafx-maven-plugin</artifactId> + <version>0.0.2</version> <configuration> <mainClass>net.javainthebox.scatterplotter.Main</mainClass> </configuration> </plugin> </plugins>
行頭の-が削除した行で、+が追加した行です。
JavaFX Pluginは以下のURLに情報があります。
これで、JavaFX SDKをダウンロードして、展開する必要がなくなります。
Exec Maven Pluginではないので、実行にはjavafx:runを使います。
C:\scatterplotter>mvn clean javafx:run
簡単ですね!
もちろん、コマンドラインからでも実行できます。Java 8で実行するときに説明したように依存するライブラリをコピーしてから実行するようにします。
C:\scatterplotter>mvn dependency:copy-dependencies [INFO] Scanning for projects... [INFO] [INFO] ------------------< net.javainthebox:scatterplotter >------------------- [INFO] Building scatterplotter 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-dependency-plugin:2.8:copy-dependencies (default-cli) @ scatterplotter --- <<省略>> C:\scatterplotter>java --module-path target\dependency --add-modules javafx.controls,javafx.fxml -cp target\scatterplotter-1.0-SNAPSHOT.jar;target\dependency\* net.javainthebox.scatterplotter.Main
Java 8の時とは異なり、dependencyディレクトリにJavaFXのJARファイルもコピーされます。このため、モジュールパスとクラスパスで同じディレクトリを指定しているのですが、これで問題なく動きます。
JREをバンドルしたパッケージを作成
最後にアプリケーション用にカスタマイズしたJREを作成して、アプリケーションパッケージを作ってみましょう。
先ほどまではJavaFX SDKを使っていたのですが、ここで使用するのはJavaFX jmodsです。jmodsもSDKと同じページからダウンロードできます。
SDKとjmodsの違いは、SDKにはJARファイルが含められていますが、jmodsの方はJMODファイルだということです。
JMODファイルなんて聞いたことがないという人も多いと思いますが、JMODファイルはDLLとかシェアードライブラリなどのネイティブライブラリを一緒にパッケージングできるファイル形式です。たとえば、JDKのjava.baseなどのモジュールもJMODファイルで提供されています。JDKのディレクトリの下にあるjmodsファイルに配置されています。
JavaFXはプラットフォームごとにネイティブのライブラリを含むので、JMODファイルの方がやりやすいのです。でも、JavaFXのJMODファイルは通常実行時には使えません。jlinkでカスタムJREを作成するときに使うことができます。
では、jlinkでカスタムJREを作成してみましょう。
ここでは、ダウンロードしたJavaFX jmodsのZIPファイルをC:\scatterplotterディレクトリに展開しました。
jlinkは使用するモジュールだけをJREにしますが、scatterplotterで直接使用しているのはjava.baseモジュール、javafx.controlsモジュール、javafx.fxmlモジュールの3つです。この中で、java.baseモジュールはJavaのアプリケーションであれば必ず使用するので、デフォルトで追加されます。
また、OpenCSVもApache Commons Mathもモジュールではないので、JREには含めません。
では、jlinkでカスタムJREを作成しましょう。
C:\scatterplotter>jlink --module-path javafx-jmods-12.0.1 --add-modules javafx.controls,javafx.fxml --compress=2 --output scatterplotter
モジュールパス(--module-path)には展開したJavaFX jmodsのディレクトリを指定します。--add-modulesは前述したようにjavafx.controlsとjavafx.fxmlをしてします。--compressオプションはJREの圧縮率を示すオプションで0, 1, 2のいずれかを指定します。0は圧縮なし、2が圧縮率が高いことになります。
最後の--outputオプションでJREの出力先ディレクトリを指定します。ここでは、scatterplotterを指定しているので、scatterplotterディレクトリにJREが作成されます。
作成したJREで以下のように実行すると、JREに組み込まれたモジュールが分かります。
C:\scatterplotter>scatterplotter\bin\java --list-modules java.base@12.0.1 java.datatransfer@12.0.1 java.desktop@12.0.1 java.prefs@12.0.1 java.scripting@12.0.1 java.xml@12.0.1 javafx.base javafx.controls javafx.fxml javafx.graphics jdk.unsupported@12.0.1
scatterplotterで直接使用するのは前述した3種類のモジュールですが、javafx.controlsとjavafx.fxmlが依存しているモジュールも芋づる式にインクルードされます。そのため、java.desktopモジュールなどがJREに含まれています。
後は、target\dependencyディレクトリにあるJavaFX以外のJARファイル群と、scatterplotterのJARファイルをscatterplotterディレクトリにコピーします。ここではどちらもscatterplotterディレクトリ直下にdependencyディレクトリを作成しして、そこにコピーしました。
最後に起動用のバッチファイルを用意しましょう。ここではscatterplotter.batとしました。内容は以下の通り。
@echo off bin\java -cp dependency\* net.javainthebox.scatterplotter.Main
ここではWindows版のJREを作ったのでバッチファイルですが、Linuxであればシェルスクリプトファイルを作ります。
C:\scatterplotter\scatterplotter>dir ドライブ C のボリューム ラベルがありません。 ボリューム シリアル番号は 6868-701E です C:\scatterplotter\scatterplotter のディレクトリ 2019/07/05 21:37 <DIR> . 2019/07/05 21:37 <DIR> .. 2019/07/05 21:17 <DIR> bin 2019/07/05 21:17 <DIR> conf 2019/07/05 21:31 <DIR> dependency 2019/07/05 21:17 <DIR> include 2019/07/05 21:17 <DIR> legal 2019/07/05 21:17 <DIR> lib 2019/07/05 21:17 182 release 2019/07/05 21:33 75 scatterplotter.bat 2 個のファイル 257 バイト 8 個のディレクトリ 868,741,357,568 バイトの空き領域 C:\scatterplotter\scatterplotter>dir dependency ドライブ C のボリューム ラベルがありません。 ボリューム シリアル番号は 6868-701E です C:\scatterplotter\scatterplotter\dependency のディレクトリ 2019/07/05 21:31 <DIR> . 2019/07/05 21:31 <DIR> .. 2019/07/04 20:10 246,174 commons-beanutils-1.9.3.jar 2019/07/04 20:10 588,337 commons-collections-3.2.2.jar 2019/07/04 20:10 752,798 commons-collections4-4.2.jar 2019/07/04 20:10 501,879 commons-lang3-3.8.1.jar 2019/07/04 20:10 61,829 commons-logging-1.2.jar 2019/07/04 20:10 2,213,560 commons-math3-3.6.1.jar 2019/07/04 20:10 182,954 commons-text-1.3.jar 2019/07/04 20:10 170,348 opencsv-4.6.jar 2019/07/04 20:18 7,929 scatterplotter-1.0-SNAPSHOT.jar 9 個のファイル 4,725,808 バイト 2 個のディレクトリ 868,741,292,032 バイトの空き領域 C:\scatterplotter\scatterplotter>
後は、JREを作成したscatterplotterディレクトリをZIPかなにかでまとめてしまえば、配布用パッケージの完成です。
ちなみに、アプリケーションがモジュールになっていると、JavaFX Maven PluginでJREを作成できるのですが、JavaFXアプリケーションをモジュールにする意味ってほとんどないんですよね。なので、ここでは非モジュールアプリケーションをサンプルにして、jlinkを直接使用する方法を説明しました。