[OpenJDK11&JavaFX13]IntelliJ+gradleでjavaFXアプリをビルドし、軽量JREと合わせて配布可能なパッケージを作成する

配布可能なJavaFXアプリを作成したかったのですが、試行錯誤したのでこちらにまとめます。

# 環境
```
$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

※macOS Mojave(10.14.6)でも同様の手順で作成できました。

$ java --version
openjdk version "11.0.4" 2019-07-16
OpenJDK Runtime Environment (build 11.0.4+11-post-Ubuntu-1ubuntu218.04.3)
OpenJDK 64-Bit Server VM (build 11.0.4+11-post-Ubuntu-1ubuntu218.04.3, mixed mode, sharing)

$ jlink --version
11.0.4
```

# JDKの選定
最初はJavaFXを同梱しているAmazon CorrettoなどのJDKを使用しようと思っていました。
しかし、以下の記事を読んで、今回はOpenjdk-11を使用し、JavaFXはライブラリとして使用し、配布時に軽量JREを生成することにしました。
[OpenJFX時代のJDK選び - もしくはOpenJFX時代のアプリケーション配布](https://skrb.hatenablog.com/entry/2019/06/24/220639)

# 手順
## IntelliJでgradleプロジェクトを作成
IntelliJで、新規gradleプロジェクトを作成します。

![Screenshot from 2019-10-01 16-05-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492708/b25d6cec-0b23-480b-35ae-deba3296b4a6.png)

## build.gradleにjavafx用の設定を追加
[OpenJFXの公式サイト](https://openjfx.io/index.html)に記載されていた以下のソースを参考に、build.gradleを作成します。
https://github.com/openjfx/samples/blob/master/IDE/IntelliJ/Modular/Gradle/hellofx/build.gradle

なお、IntelliJの設定でもともと設定で「Automattically import this project on changes in build script files」にチェックを入れていますので、よしなにモジュールはインポートされます。

```build.gradle
plugins {
    id 'java'
    id "org.openjfx.javafxplugin" version "0.0.8" // 追加
}

group 'com.example'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

// 追加
javafx {
    version = "13"
    modules = [ 'javafx.controls', 'javafx.fxml' ]
}

// 実行可能Jarを作成する際のMainクラスを指定
jar {
    manifest {
        attributes 'Main-Class': 'Main'
    }
}
```

## テスト用のJavaFxクラスを作成
```Main.java
import javafx.application.Application;

public class Main {
    public static void main(String... args){
        Application.launch(MyApplication.class);
    }
}
```
```MyApplication.java
import javafx.application.Application;
import javafx.stage.Stage;

public class MyApplication extends Application {
    public MyApplication(){
        super();
    }

    @Override
    public void start(Stage primaryStage) throws Exception{
        primaryStage.setTitle("Hello");
        primaryStage.setWidth(300);
        primaryStage.setHeight(200);
        primaryStage.show();
    }
}
```
こちらを実行した結果、以下の画面が立ち上がります。
![Screenshot from 2019-10-01 16-43-52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492708/eaae57cf-8c4c-6360-040c-cd810723b12e.png)


## buildして、実行可能JARファイルを作成
コンソールにて、```gradle build```を実行します。
特に問題がなければ、実行可能jarファイルが作成されるかと思います。
![Screenshot from 2019-10-01 16-47-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492708/3df328ec-b352-53a9-f411-4c7e63afe297.png)

この状態で、jarファイルを実行してみると・・・
![Screenshot from 2019-10-01 16-48-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492708/12789cf0-e8e6-c79e-5f9d-7f8e21553acf.png)

このように、javafx.application.Applicationクラスが無いよと怒られます。
これは、openjdk-11.0.4のjre内に、JavaFXモジュールが存在していないからですね。

## 軽量JREを作成する
では、jlinkを使って今回のjarファイルを動作させるための軽量jreを作成していきましょう！
今回は、以下の記事を参考にさせていただきました。
[配布用の軽量 JRE の作り方](https://qiita.com/Targityen/items/8a136eb4792f669a1122)

まず、以下ページのDownloadsより、jmodsをダウンロードします。
https://openjfx.io/
![Screenshot from 2019-10-01 16-54-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492708/58e6548e-6f1c-1d8e-b977-ffadfc40d7cc.png)

ダウンロードしたzipファイルを展開し、適当なパスに配置します。
(今回は、```/usr/lib/jvm/javafx-13-openjfx/javafx-jmods-13```に配置しています。)

なお、今回はLinux用jmodsをダウンロードしました。
※各実行環境のOSに合わせ、モジュールをダウンロードしましょう。

次に、```jdeps ${jarファイルパス}```コマンドにて、今回のjarファイルを実行するために必要なモジュールを調べます。
![Screenshot from 2019-10-01 16-56-53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492708/9a9eb201-2c38-d439-9501-253768f51439.png)

今回は、以下のモジュールを使えば良さそうです。
・java.lang -> java-base
・java.application -> javafx.base
・javafx.stage -> javafx.controls

※本当は、```jdeps --list-deps ${jarファイルパス}```とすると、どのモジュールを使えばよいかわかるようですが、何故かこのオプションが使用できず・・・。

必要なモジュールがわかったので、軽量jreを作成します。
以下のjlinkコマンドで、上記のモジュールを指定して作成できました。

```
$ jlink --compress=2 --module-path /usr/lib/jvm/java-11-openjdk-amd64/jmods:/usr/lib/jvm/javafx-13-openjfx/javafx-jmods-13 --add-modules java.base,javafx.base,javafx.controls --output jre
```
カレントパスにjreディレクトリが作成されました。
```jre/bin/java```のバイナリも作成されています。

![Screenshot from 2019-10-01 17-23-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492708/cdbbb18c-3aa3-facf-3dbf-0678088ed4da.png)

## 作成したJREを使用して、JARファイルを実行する。
では、最後に作成したjreを利用して、プログラムを実行してみましょう。

```
$ ./jre/bin/java -jar build/libs/javafx-sample-1.0-SNAPSHOT.jar 
```

![Screenshot from 2019-10-01 17-30-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492708/da7c9db6-c2a6-705a-7ffe-e7b98cb613fc.png)

無事に実行できました！

あとはJARファイルとjreをよしなにディレクトリにまとめ、実行用のシェルやbatファイルを作成するなり、appパッケージにまとめるなりすると完成です。
(以下は、フォルダにまとめ、シェルを作った例)

```
$ ls
javafx-sample-1.0-SNAPSHOT.jar  jre  start.sh

$ cat start.sh
#!/bin/sh
BASE=$(cd $(dirname $0); pwd)
$BASE/jre/bin/java -jar $BASE/javafx-sample-1.0-SNAPSHOT.jar

## sh start.shで実行可能
```

#お疲れ様でした！
初めてjlinkやjdepsなどのコマンドを使い軽量JREを作成しましたが、使用者の環境(jdkなど)に依存しないで実行できるJavaFXアプリを作る際は、この方法が簡単かなと思います。
なお、今回のjreディレクトリのサイズは368.3MBなので、もっと色々できるようにすると重たくなってしまうのかな？

以上です。ありがとうございました！