JavaFXのインストーラーを作成する手順

Java

JavaFX(OpenJFX)アプリケーションの配布用インストーラーを作成する手順です。
試行錯誤の末どうにかJava 14のjpackageを使ってWindows環境用のインストーラーを作成することができました。

準備

Java 14のインストール

AdoptOpenJDKのサイトから “OpenJDK 14 (Latest)” をダウンロードしてインストールします。
https://adoptopenjdk.net/

インストールするとJava 14が有効になります。

> java -version
openjdk version "14" 2020-03-17
OpenJDK Runtime Environment AdoptOpenJDK (build 14+36)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14+36, mixed mode, sharing)

Java 14のjpackageを使いたいだけなので環境変数のPathからJava 14のパスは削除しました。
基本的にはLTSバージョンであるJava 11を使用します。

Java 11については JAVA_HOME で設定しており、Pathから JAVA_HOME を参照しています。

Java 11が有効なことを確認します。

> java -version
openjdk version "11.0.6" 2020-01-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.6+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.6+10, mixed mode)

WiX Toolsetのインストール

Chocolateyから Wix Toolset をインストールします。
https://wixtoolset.org/releases/

JavaFXプロジェクトの作成

EclipseでJavaFXのサンプルプロジェクトを作成します。
ファイル -> 新規 -> その他 から「Mavenプロジェクト」を選択して「次へ」進みます。

「javafx」のアーキタイプを検索して「次へ」進みます。

JavaFX、Mavenプラグインのバージョンはそれぞれ最新のバージョンに変更しました。

こんな感じのプロジェクトが作成されました。
「module-info.java」があり、モジュール化されているアプリケーションです。

実行するとJavaFXのサンプルアプリケーションが起動します。
(App.javaを右クリックして 実行 -> Javaアプリケーション)

Maven Wrapperのインストール

Windows 32bit環境でもインストーラーを作成するのでMaven Wrapperをインストールします。

> mvn -N io.takari:maven:wrapper

インストーラーの作成(シンプルなプロジェクト)

コマンドプロンプトでjavafxプロジェクトの直下に移動し、packageコマンドでJARファイルを作成します。
成功すると「target」フォルダ内に「javafx-0.0.1-SNAPSHOT.jar」が作成されます。

> mvnw.cmd clean package

アプリケーション配布用のJREを作成するjlinkは「javafx-maven-plugin」がやってくれるので、「javafx:jlink」コマンドを実行します。
成功すると「target」フォルダ内に「image」が作成され、これが配布用のJREになります。

> mvnw.cmd javafx:jlink

配布用のJRE(image)でJavaFXアプリケーションが起動することを確認します。

> target\image\bin\java sample.javafx.App

「jpackage」コマンドでインストーラーを作成します。
Java 14のパスを通していないのでフルパスでjpackageを使用していますが、パスを通している環境であれば「jpackage」に置き換えられると思います。

成功するとプロジェクト直下にインストーラー(javafx-1.0.exe)が作成されます。
今回作成されたインストーラーは43MBほどでした。

> "C:\Program Files\AdoptOpenJDK\jdk-14.0.0.36-hotspot\bin\jpackage.exe" ^
  --type exe ^
  --input target ^
  --name javafx ^
  --main-class sample.javafx.App ^
  --main-jar javafx-0.0.1-SNAPSHOT.jar ^
  --runtime-image target/image ^
  --vendor "sample" ^
  --win-shortcut

引数の説明はこちらにありますが、まだ理解できていないところが多いです。
JEP 343: Packaging Tool (Incubator)

今のところの理解はこんな感じです。

–type インストーラーの形式を指定する(exe、msi、app-imageなど)
–inputJARファイルがある場所を指定する
–nameアプリケーション名を指定する
–main-classMainクラスを指定する(sample.javafx.Appクラス)
–main-jarJARファイルを指定する(mvn packageで作成したJAR)
–runtime-image配布用JREがある場所を指定する(mvn javafx:jlinkで作成したJRE)
–vendor配布元の開発者や会社名を指定する
–win-shortcutデスクトップにショートカットを作成する

インストールしてみる

インストーラー(javafx-1.0.exe)を実行すると「C:\Program Files\javafx」にインストールが行われ、デスクトップにショートカットが作成されます。

ショートカットをダブルクリックするとサンプルアプリケーションが起動します。

「アプリと機能」からアンインストールもできます。

インストーラーの作成(複雑なプロジェクト)

上記のシンプルなプロジェクトの構成では下記の問題があることが分かりました。

  • モジュール化されていないライブラリは jlink でカスタムJREに含めることができない。
  • Maven Repositoryに登録されているJavaFX(OpenJFX)はWindows 64bit環境用で32bit環境で動作しない。

モジュール化されていないライブラリはカスタムJREに含められないようなので、依存するライブラリは基本的にUber-Jar(Fat-Jar) を作成して含めることにしました。
JavaFXも依存するライブラリですが、実行環境(Windows 64bit、32bit等)で異なるのでUber-Jarには含めずにjlinkで作成するカスタムJREに含めることにしました。

Uber-Jar (Fat-Jar) の作成

JavaFXはUber-Jarに含めたくないのでpom.xmlでscopeをprovidedに変更します。

<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-controls</artifactId>
    <version>14.0.1</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-fxml</artifactId>
    <version>14.0.1</version>
    <scope>provided</scope>
</dependency>

Maven Shade Plugin を使って Uber-Jar を作成します。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.3</version>
    <configuration>
        <createDependencyReducedPom>false</createDependencyReducedPom>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>sample.javafx.App</mainClass>
                    </transformer>
                </transformers>
                <outputFile>${project.build.directory}/shade/${project.artifactId}-${project.version}-uber.jar</outputFile>
            </configuration>
        </execution>
    </executions>
</plugin>

mvn packageを実行すると Uber-Jar が target/shade に作成されます。

> mvnw.cmd clean package

JavaFXのjmodsのダウンロード

使用しているJavaFXのバージョンと実行環境に合った jmods をダウンロードします。
Windows 64bit環境なら「JavaFX Windows x64 jmods」、32bit環境なら「JavaFX Windows x86 jmods」をダウンロードします。
https://gluonhq.com/products/javafx/

ダウンロードしたzipファイルを解凍して C:\Program Files\Java に置きました。

カスタムJREの作成

jdepsコマンドで依存しているモジュールを確認します。

> jdeps -summary "target\shade\javafx-0.0.1-SNAPSHOT-uber.jar"
javafx-0.0.1-SNAPSHOT-uber.jar -> JDK removed internal API
javafx-0.0.1-SNAPSHOT-uber.jar -> java.base
javafx-0.0.1-SNAPSHOT-uber.jar -> java.logging
javafx-0.0.1-SNAPSHOT-uber.jar -> java.management
javafx-0.0.1-SNAPSHOT-uber.jar -> java.naming
javafx-0.0.1-SNAPSHOT-uber.jar -> java.sql
javafx-0.0.1-SNAPSHOT-uber.jar -> java.xml
javafx-0.0.1-SNAPSHOT-uber.jar -> 見つかりません

jlinkコマンドでカスタムJREを作成します。
「–add-modules」はカンマ区切りで1つにまとめても大丈夫です。

「jdk.charsets」や「jdk.localedata」のモジュールは使用していてもjdepsコマンドで分析されないので、使用する場合は意図的に含める必要があります。
「–add-modules “jdk.localedata” –include-locales=ja」はjaとすることで日本語ロケールのみ含めています。

> jlink ^
  --module-path "C:\Program Files\Java\javafx-jmods-14.0.1" ^
  --add-modules "java.base,java.logging,java.management,java.naming,java.sql,java.xml" ^
  --add-modules "javafx.controls,javafx.fxml" ^
  --add-modules "jdk.charsets" ^
  --add-modules "jdk.localedata" --include-locales=ja ^
  --compress=2 --no-header-files --no-man-pages ^
  --output "target\jre"

「java –list-modules」でカスタムJREに含めたモジュールが確認できます。

> target\jre\bin\java --list-modules
java.base@11.0.7
java.datatransfer@11.0.7
java.desktop@11.0.7
java.logging@11.0.7
java.management@11.0.7
java.naming@11.0.7
java.prefs@11.0.7
java.scripting@11.0.7
java.security.sasl@11.0.7
java.sql@11.0.7
java.transaction.xa@11.0.7
java.xml@11.0.7
javafx.base
javafx.controls
javafx.fxml
javafx.graphics
jdk.charsets@11.0.7
jdk.localedata@11.0.7
jdk.unsupported@11.0.7

依存関係を細かく気にしない場合はJavaFXのモジュールを全部含めて「–bind-services」で全部入りのカスタムJREを作成できるようですが、ファイルサイズが約60MBから約146MBと2倍以上にふくれあがってしまうのでやめました。

> jlink --module-path "C:\Program Files\Java\javafx-jmods-14.0.1" ^
  --add-modules "javafx.base,javafx.controls,javafx.fxml,javafx.graphics,javafx.media,javafx.swing,javafx.web" ^
  --bind-services ^
  --compress=2 ^
  --no-header-files ^
  --no-man-pages ^
  --output "target\jre"
> target\jre\bin\java --list-modules
java.base@11.0.7
java.compiler@11.0.7
java.datatransfer@11.0.7
java.desktop@11.0.7
java.logging@11.0.7
java.management@11.0.7
java.management.rmi@11.0.7
java.naming@11.0.7
java.net.http@11.0.7
java.prefs@11.0.7
java.rmi@11.0.7
java.scripting@11.0.7
java.security.jgss@11.0.7
java.security.sasl@11.0.7
java.smartcardio@11.0.7
java.xml@11.0.7
java.xml.crypto@11.0.7
javafx.base
javafx.controls
javafx.fxml
javafx.graphics
javafx.media
javafx.swing
javafx.web
jdk.accessibility@11.0.7
jdk.charsets@11.0.7
jdk.compiler@11.0.7
jdk.crypto.cryptoki@11.0.7
jdk.crypto.ec@11.0.7
jdk.crypto.mscapi@11.0.7
jdk.dynalink@11.0.7
jdk.internal.opt@11.0.7
jdk.jartool@11.0.7
jdk.javadoc@11.0.7
jdk.jdeps@11.0.7
jdk.jfr@11.0.7
jdk.jlink@11.0.7
jdk.jsobject@11.0.7
jdk.localedata@11.0.7
jdk.management@11.0.7
jdk.management.jfr@11.0.7
jdk.naming.dns@11.0.7
jdk.naming.rmi@11.0.7
jdk.scripting.nashorn@11.0.7
jdk.security.auth@11.0.7
jdk.security.jgss@11.0.7
jdk.unsupported@11.0.7
jdk.unsupported.desktop@11.0.7
jdk.xml.dom@11.0.7
jdk.zipfs@11.0.7

インストーラーの作成

Windows 32bit環境ではjpackageコマンドで作成したインストーラーはエラーになりインストールできませんでした。アプリケーションイメージからアプリケーションを起動することができるので、それをZIP圧縮して配布することにしました。

そのためまずはアプリケーションイメージを作成します。

> set JPACKAGE_HOME=C:\Program Files\AdoptOpenJDK\jdk-14.0.0.36-hotspot
> "%JPACKAGE_HOME%\bin\jpackage" ^
  --type "app-image" ^
  --name "javafx" ^
  --input "target\shade" ^
  --main-class "sample.javafx.App" ^
  --main-jar "javafx-0.0.1-SNAPSHOT-uber.jar" ^
  --runtime-image "target\jre" ^
  --vendor "Fire Sign" ^
  --icon "src/main/resources/icon.ico" ^
  --app-version "0.0.1" ^
  --java-options "-Xmx128m" ^
  --copyright "Copyright 2020 Fire Sign" ^
  --temp "target\temp-app-image" ^
  --dest "target\installer"

Windows 64bit環境ではさらにアプリケーションイメージを元にインストーラーを作成します。

> "%JPACKAGE_HOME%\bin\jpackage" ^
  --type "msi" ^
  --app-image "target\installer\javafx" ^
  --name "javafx" ^
  --vendor "Fire Sign" ^
  --app-version "0.0.1" ^
  --copyright "Copyright 2020 Fire Sign" ^
  --win-menu ^
  --win-menu-group "Sample Group" ^
  --win-per-user-install ^
  --win-shortcut ^
  --temp "target\temp-msi" ^
  --dest "target\installer"

インストーラーを作成するバッチファイル

毎回複数のコマンドを打つのは大変なので一連の処理をバッチファイルにまとめます。

build-win.bat

@ECHO OFF

set UBER_JAR=%ARTIFACT_ID%-%PROJECT_VERSION%-uber.jar

jlink ^
  --module-path "%JFX_MODS%" ^
  --add-modules "%JDK_MODULES%,%JFX_MODULES%,jdk.charsets,jdk.localedata" ^
  --include-locales=ja ^
  --compress=2 ^
  --no-header-files ^
  --no-man-pages ^
  --output "target\jre"


"%JPACKAGE_HOME%\bin\jpackage" ^
  --type "app-image" ^
  --name "%APP_NAME%" ^
  --input "target\shade" ^
  --main-class "%MAIN_CLASS%" ^
  --main-jar "%UBER_JAR%" ^
  --runtime-image "target\jre" ^
  --vendor "%VENDOR%" ^
  --icon "%ICON%" ^
  --app-version "%APP_VERSION%" ^
  --java-options "%JAVA_OPTIONS%" ^
  --copyright "%COPYRIGHT%" ^
  --temp "target\temp-app-image" ^
  --dest "target\installer"


for %%t in ("msi" "exe") do call "%JPACKAGE_HOME%\bin\jpackage" ^
  --type %%t ^
  --app-image "target\installer\%APP_NAME%" ^
  --name "%APP_NAME%" ^
  --vendor "%VENDOR%" ^
  --app-version "%APP_VERSION%" ^
  --copyright "%COPYRIGHT%" ^
  --win-menu ^
  --win-menu-group "%MENU_GROUP%" ^
  --win-per-user-install ^
  --win-shortcut ^
  --temp "target\temp-%%t" ^
  --dest "target\installer"


powershell compress-archive "'target\installer\%APP_NAME%\*'" "'target\installer\%APP_NAME%.zip'"

Exec Maven Plugin を使用して「mvnw.cmd clean package」実行時にバッチファイルが実行され、インストーラーが作成されるようになりました。

pom.xml 全体

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>sample</groupId>
    <artifactId>javafx</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <javafx.version>14.0.1</javafx.version>
        <lombok.version>1.18.12</lombok.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>${javafx.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- MapStruct は Lombok v1.18.12 に必要 -->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.3.1.Final</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <release>11</release>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                             <version>${lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.3</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>sample.javafx.App</mainClass>
                                </transformer>
                            </transformers>
                            <outputFile>${project.build.directory}/shade/${project.artifactId}-${project.version}-uber.jar</outputFile>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <profiles>
        <profile>
            <id>build-windows</id>
            <activation>
                <os>
                    <family>windows</family>
                </os>
            </activation>
            <build>
                <plugins>
                    <plugin>
                        <artifactId>exec-maven-plugin</artifactId>
                        <groupId>org.codehaus.mojo</groupId>
                        <version>1.6.0</version>
                        <executions>
                            <execution>
                                <phase>package</phase>
                                <goals>
                                    <goal>exec</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <workingDirectory>${project.basedir}</workingDirectory>
                            <executable>build-win.bat</executable>
                            <environmentVariables>
                                <ARTIFACT_ID>${project.artifactId}</ARTIFACT_ID>
                                <PROJECT_VERSION>${project.version}</PROJECT_VERSION>
                                <JPACKAGE_HOME>C:\Program Files\AdoptOpenJDK\jdk-14.0.0.36-hotspot</JPACKAGE_HOME>
                                <JFX_MODS>C:\Program Files\Java\javafx-jmods-14.0.1</JFX_MODS>
                                <JDK_MODULES>java.base,java.logging,java.management,java.naming,java.sql,java.xml</JDK_MODULES>
                                <JFX_MODULES>javafx.controls,javafx.fxml</JFX_MODULES>
                                <APP_NAME>Sample Application</APP_NAME>
                                <MAIN_CLASS>sample.javafx.App</MAIN_CLASS>
                                <VENDOR>Fire Sign</VENDOR>
                                <ICON>src/main/resources/icon.ico</ICON>
                                <APP_VERSION>0.0.1</APP_VERSION>
                                <JAVA_OPTIONS>-Xmx128m</JAVA_OPTIONS>
                                <COPYRIGHT>Copyright 2020 Fire Sign</COPYRIGHT>
                                <MENU_GROUP>Sample Group</MENU_GROUP>
                            </environmentVariables>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

その他つまづいたところ等

「–vendor」指定がないときのエラー

jpackageコマンドで「–vendor」の指定がないとIOExceptionで失敗してしまいます。

> "C:\Program Files\AdoptOpenJDK\jdk-14.0.0.36-hotspot\bin\jpackage.exe" --type exe --input target --name javafx --main-class sample.javafx.App --main-jar javafx-0.0.1-SNAPSHOT.jar --runtime-image target/image --temp "C:\temp\javafx"
WARNING: Using incubator modules: jdk.incubator.jpackage
java.io.IOException: Command [light.exe, -nologo, -spdb, -ext, WixUtilExtension, -out, C:\temp\javafx\images\win-exe.image\javafx-1.0.msi, -loc, C:\temp\javafx\config\MsiInstallerStrings_en.wxl, C:\temp\javafx\wixobj\main.wixobj, C:\temp\javafx\wixobj\bundle.wixobj]in C:\temp\javafx\images\win-msi.image\javafx exited with 311 code

Wix Toolsetをインストールしていないときのエラー

Wix Toolsetのインストールが必要です。

> "C:\Program Files\AdoptOpenJDK\jdk-14.0.0.36-hotspot\bin\jpackage.exe" --type exe --input target --name javafx --main-class sample.javafx.App --main-jar javafx-0.0.1-SNAPSHOT.jar --runtime-image target/image --vendor "sample"
WARNING: Using incubator modules: jdk.incubator.jpackage
WiXツール(light.exe、candle.exe)が見つかりません
WiX 3.0以降をhttps://wixtoolset.orgからダウンロードし、PATHに追加します。
エラー: 無効またはサポートされていないタイプ: [exe]

エラー: jlinkでは自動モジュールは使用できません

JavaFX Maven Pluginの jlink で、モジュール化されていないライブラリ(自動モジュール)を使用しているとエラーになってしまうようです。

まだモジュール化対応されていないライブラリを使いたかったのでこのプラグインを使うのをあきらめて Uber Jar (Fat Jar) で対応することにしました。

> mvn javafx:jlink
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< sample:sample-jlink >-------------------------
[INFO] Building sample-jlink 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- javafx-maven-plugin:0.0.4:jlink (default-cli) @ sample-jlink ---
エラー: jlinkでは自動モジュールは使用できません: file:///C:/Users/user/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jarからのorg.slf4j
[ERROR] Command execution failed.
org.apache.commons.exec.ExecuteException: Process exited with an error: 1 (Exit value: 1)
    at org.apache.commons.exec.DefaultExecutor.executeInternal (DefaultExecutor.java:404)
    at org.apache.commons.exec.DefaultExecutor.execute (DefaultExecutor.java:166)
    at org.openjfx.JavaFXBaseMojo.executeCommandLine (JavaFXBaseMojo.java:504)
    at org.openjfx.JavaFXBaseMojo.executeCommandLine (JavaFXBaseMojo.java:394)
    at org.openjfx.JavaFXJLinkMojo.execute (JavaFXJLinkMojo.java:187)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:210)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:957)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:289)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:193)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:566)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
org.apache.commons.exec.ExecuteException: Process exited with an error: 1 (Exit value: 1)
        at org.apache.commons.exec.DefaultExecutor.executeInternal(DefaultExecutor.java:404)
        at org.apache.commons.exec.DefaultExecutor.execute(DefaultExecutor.java:166)
        at org.openjfx.JavaFXBaseMojo.executeCommandLine(JavaFXBaseMojo.java:504)
        at org.openjfx.JavaFXBaseMojo.executeCommandLine(JavaFXBaseMojo.java:394)
        at org.openjfx.JavaFXJLinkMojo.execute(JavaFXJLinkMojo.java:187)
        at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:210)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:156)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:148)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
        at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
        at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
        at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
        at org.apache.maven.cli.MavenCli.execute(MavenCli.java:957)
        at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:289)
        at org.apache.maven.cli.MavenCli.main(MavenCli.java:193)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225)
        at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406)
        at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.830 s
[INFO] Finished at: 2020-04-04T12:18:01+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.openjfx:javafx-maven-plugin:0.0.4:jlink (default-cli) on project sample-jlink: Error: Command execution failed. Process exited with an error: 1 (Exit value: 1) -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

エラー: JavaFXランタイム・コンポーネントが不足しており、このアプリケーションの実行に必要です

Gradleプロジェクトではmainメソッドがあるクラスで「javafx.application.Application」を継承しているとエラーになってしまうようです。
JavaFX 11 : Create a jar file with Gradle – Stack Overflow

mainメソッドがあるHelloFXクラスが「javafx.application.Application」を継承していると実行することができないので、Launcherクラスを作成してそこから起動すると回避できます。

import javafx.application.Application;

public class Launcher {

	public static void main(String[] args) {
		Application.launch(HelloFX.class);
	}
}
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class HelloFX extends Application {

	@Override
	public void start(Stage stage) {
		Scene scene = new Scene(new StackPane(new Label("Hello, world!")), 640, 480);
		stage.setScene(scene);
		stage.show();
	}

//	public static void main(String[] args) {
//		launch();
//	}
}

日本語ロケールの不足

jlinkで作成したカスタムJREに日本語ロケールが不足していて、日付のフォーマットが思ったようになりませんでした。

下記を実行して問題なければ「令和2年1月1日 水曜日」になりますが、日本語ロケールが不足していると「Reiwa2年1月1日 Wed曜日」となってしまいます。

log.debug("日本語ロケールチェック:\t{}", DateTimeFormatter.ofPattern("GGGGy年M月d日 E曜日", Locale.JAPAN).format(JapaneseDate.of(2020, 1, 1)));

jlinkに必要なモジュールを確認する jdeps コマンドでは依存するロケールのモジュールは分析されません。「jdk.localedata」を追加する必要があります。
それと全てのロケールをカスタムJREに含めるとサイズが大きくなってしまうので、「–include-locales=ja」として必要なローケールだけ(ja=日本語)に絞ることができます。

--add-modules jdk.localedata --include-locales=ja

java.nio.charset.UnsupportedCharsetException: ISO-2022-JP

ロケールと同じように標準エンコーディングに含まれていない拡張エンコーディングを使用していてもjdepsコマンドでは分析されません。拡張エンコーディングを使用する場合はjlinkで「jdk.charsets」を追加する必要があります。

標準エンコーディングは java.nio.charset.Charset クラスに記載があり、「US-ASCII、ISO-8859-1、UTF-8、UTF-16BE、UTF-16LE、UTF-16」です。
https://docs.oracle.com/javase/jp/9/docs/api/java/nio/charset/Charset.html

try {
    log.debug("エンコーディングチェック:\t{}", Charset.forName("ISO-2022-JP"));
} catch (UnsupportedCharsetException e) {
    log.error("error", e);
}
21:50:56.291 [main] ERROR sample.javafx.App - error
java.nio.charset.UnsupportedCharsetException: ISO-2022-JP
        at java.base/java.nio.charset.Charset.forName(Charset.java:529)
        at sample.javafx.App.main(App.java:44)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)

Graphics Device initialization failed for : d3d, sw

Windowsの 32bit 環境で実行したときのエラーです。
Java11の jlink で作成したカスタムJREを元にjpackageでアプリケーションイメージを作成するとエラーになり、Java14で作成したカスタムJREではエラーにならず起動することができました。

それぞれ作成したJREのbin内のファイル数がJava14では73個なのに対して、Java11では31個と全然違うのでその辺に問題がありそうですが、原因は分かっていません。

> runtime\bin\java -jar app\sample-javafx-0.0.1-SNAPSHOT-uber.jar
Graphics Device initialization failed for :  d3d, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:280)
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:244)
        at javafx.graphics/com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:260)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:409)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:94)
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
        at java.base/java.lang.Thread.run(Thread.java:834)
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: No toolkit found
        at javafx.graphics/com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:272)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:409)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
        ... 5 more

このインストールパッケージはこの種類のプロセッサでサポートされていません。プロダクトベンダーに問い合わせてください。

Windows の 32bit 環境で作成したインストーラーを実行したときのエラーです。
32bit 環境のインストーラーの作成はあきらめて jpackageで作成したアプリケーションイメージをZIPで固めて配布することにしました。

タイトルとURLをコピーしました