JavaFXアプリケーションのJDK11対応(Windowsインストーラー作成編)
はじめに
JDK 10までは、JavaFXアプリケーションを配布する際に、ネイティブ・インストーラー形式にするjavapackager機能を使ってOS固有のインストーラーを作成することができました。Windowsネイティブなインストーラーを作成する際には、外部ツールとしてWiX Toolsetを使ってMSI形式のインストーラーを作成するか、同じく外部ツールとしてInnoSetupを使ってEXE形式のインストーラーを作成することができました。
しかし、JDK 11ではJavaFXとともにjavapackager機能が分離したため、JDK11上でのJavaFXアプリケーションは独自にインストーラーを作成することになります。
今回は、次のJavaFXアプリケーションをWindows OS上へインストールするMSI形式のインストーラーを作成していきます。gitのブランチは、jdk11 となります。masterは、JDK 8版のままです。
環境
項目 | 内容 |
---|---|
OS | Windows 10 64bit |
JDK | OpenJDK 11.0.1 |
インストーラー作成ツール | WiX Toolset 3.10 |
WiX Toolsetの概要とインストール方法は、次のWikiページに記載しています。
http://www.torutk.com/projects/swe/wiki/WiX
JavaFXアプリケーションの実行イメージ作成
インストーラーを作成する前に、JavaFXアプリケーションの実行イメージをjlinkコマンドを使って作成しておきます。
D:\work\AnalogClockGadget> jlink ^ --module-path "C:\Program Files\Java\JavaFX\javafx-jmods-11.0.1";build\modules ^ --add-modules com.torutk.gadget.analogclock ^ --launcher analogclock=com.torutk.gadget.analogclock/com.torutk.gadget.analogclock.AnalogClockApp ^ --compress=2 --no-header-files --output runtime D:\work\AnalogClockGadget> dir /w runtime [.] [..] [bin] [conf] [legal] [lib] release D:\work\AnalogClockGadget>
- JavaFXライブラリ(JMODモジュールファイル)と、ビルドしたアプリケーション(モジュール)を--module-pathオプションで指定します。
- モジュール依存の基点となるJavaFXアプリケーション(モジュール)を--add-modulesオプションで指定します。
- JavaFXアプリケーション実行用スクリプト/バッチファイルを生成するため、--launcherオプションでモジュールとエントリポイントクラス名を指定します。ここではスクリプト/バッチファイルの名称をanalogclockとし、モジュール名/エントリポイントクラス名を指定します。
- 生成する実行イメージの大きさを小さくするため、--compressオプションでZIP圧縮(=2)を指定します。
- 実行イメージにはJNI用のC/C++ヘッダーファイルを含まないよう--no-header-filesオプションを指定します。
生成されたruntimeディレクトリのbin\analogclock.bat を実行し、アプリケーションが起動することを確認します。
インストーラーの作成(第一歩)
WiX Toolsetを使って、JavaFXアプリケーションのインストーラーを作成します。まずは、最低限の機能だけを持つインストーラーを作成します。
インストーラー作成では、WiXのXML文書を記述します。今回は、インストールする内容、インストール先ディレクトリを定義するAnalogClock.wxsファイルと、インストールするファイル群をheatコマンドで生成したruntime.wxsファイルを用意します。
WiX用XMLドキュメント(AnalogClock.wxs)
AnalogClock.wsx(左端の▼印をクリックすると内容を表示/非表示)
<?xml version="1.0" encoding="utf-8"?> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> <Product Id="*" Name="Analog Clock" Language="1041" Codepage="932" Version="0.4.1" Manufacturer="High Bridge" UpgradeCode="fe287b25-e03d-4744-90f4-96b05dc36bf0"> <Package Description="Analog Clock Gadget" Comments="(c) 2019 High Bridge" InstallerVersion="200" Compressed="yes" InstallScope="perMachine" /> <MajorUpgrade DowngradeErrorMessage="既に新しい [ProductName]がインストールされています。"/> <MediaTemplate EmbedCab="yes" /> <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFiles64Folder"> <Directory Id="CompanyFolder" Name="High Bridge"> <Directory Id="ApplicationFolder" Name="AnalogClock" /> </Directory> </Directory> </Directory> <Feature Id="Product" Title="Analog Clock" Level="1"> <ComponentGroupRef Id="RuntimeGroup" /> </Feature> </Product> </Wix>
Wix要素
WiXのXML文書構造は、最上位の要素がWixで、Wix要素の属性で名前空間を指定しています。
<?xml version="1.0" encoding="utf-8"?> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"> : </Wix>
Product要素
Wixの子要素にProduct要素を記述し、アプリケーション名などを定義します。
<Product Id="*" Name="Analog Clock" Language="1041" Codepage="932" Version="0.4.1" Manufacturer="High Bridge" UpgradeCode="fe287b25-e03d-4744-90f4-96b05dc36bf0"> : </Product>
- Id属性にはGUIDを指定しますが、"*"を指定するとコンパイル時にGUIDが自動生成されます。インストーラーはこのGUIDが等しいと同じプログラムと認識します。新しいバージョンなどインストーラーを作り直した際はこのGUIDを変更する必要があります。
- Name属性にはアプリケーション名を定義します。
- Version属性にはアプリケーションのバージョン番号を定義します。通常ピリオドで区切った4つの数値を指定します。ただしMSIインストーラーはバージョンアップ判定時に4つ目の数字を無視することに留意が必要です。
- Language属性とCodepage属性には、インストーラーが使用するロケールと文字コードを定義します。日本語の場合は、Language属性に1041、Copdepage属性に932を指定します。
- Manufacturer属性には開発元の組織名または開発者名を定義します。
- UpgradeCode属性には同じアプリケーションで新しいバージョンを更新インストールする際の識別に使うGUIDを定義します。このGUIDは後々新しいバージョンのインストーラーを作成するときに継続して使用します。GUIDを生成する方法については、Windowsデスクトップ環境でUUID(GUID)を生成する方法 - torutkのブログ や、 GUIDの生成 に記載しています。
Package要素
Productの子要素にPackage要素を記述します。
<Package Description="Analog Clock Gadget" Comments="(c) 2019 High Bridge" InstallerVersion="200" Compressed="yes" InstallScope="perMachine"/>
- Description属性とComments属性は、プログラムの説明、コメントを記載します。インストーラーファイルのプロパティ(詳細)で説明に表示されます。
- InstallerVersion属性はMSIインストーラー(msiexec.exe)のバージョンを指定します。MSIのバージョンに2.0を指定する場合、"200"とします。現時点では4.5を指定("405")してもいいかと思います。MSI 4.5は、Windows Vista SP2、Windows Server 2008 SP2に標準搭載されています。
- InstallScope属性は、全てのユーザーが使用できるperMachineか、インストールしたユーザーだけが使用できるperUserかを指定します。
Platform属性は、インストールするパッケージが64bit用であればx64を指定します。この属性でx64を指定すると、Component要素全てにWin64属性の指定が必要になります。そこで、64bitプログラムをインストールする場合は、この属性ではなく、candleコマンドの-archオプションでx64を指定します。
MajorUpgrade要素
新しいバージョンへ更新する仕組みを入れます。
<MajorUpgrade DowngradeErrorMessage="既に新しい [ProductName]がインストールされています。"/>
DowngradeErrorMessage属性には、既にインストールされたバージョンより古いバージョンをインストールしようとしたときにエラーメッセージとして表示する内容を定義します。
MediaTemplate要素
MSIファイルの中にCABファイルを含める場合に指定します(ふつう含めるので)。
<MediaTemplate EmbedCab="yes" />
Directory要素
インストーラーでインストールする先のディレクトリ構造を定義します。 今回は、次のディレクトリにインストールするものとします。
C:\ +-- Program Filse +-- High Bridge +-- AnalogClock
このディレクトリ構造に対応するDirectory要素の定義は次となります。
<Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFiles64Folder"> <Directory Id="CompanyFolder" Name="High Bridge"> <Directory Id="ApplicationFolder" Name="AnalogClock" /> </Directory> </Directory> </Directory>
- ルートディレクトリ(ドライブ)は、Id属性にTARGETDIRを指定します。
- 64bitアプリがインストールされるシステム共通のディレクトリは C:\Program Files ですが、これは事前定義名ProgramFiles64FolderをId属性に指定します。
- C:\Program Filesの下に作成するディレクトリを指定します。
Feature要素
<Feature Id="Product" Title="Analog Clock" Level="1"> <ComponentGroupRef Id="RuntimeGroup" /> </Feature>
Feature要素ではインストールする内容を定義します。今回は、インストールするファイル群をWiX Toolsetのheatコマンドで自動生成させるので、heatコマンドで作成するComponentGroupのIdを、ComponentGroupRefで参照する記述とします。なお、ComponentGroupのIdはheatコマンドのオプションで指定します。
WiX Toolsetコマンドの実行
heatコマンドでruntimeディレクトリ以下のファイル群から定義作成
D:\work\AnalogClockGadget> heat dir runtime ^ -srd -dr ApplicationFolder ^ -cg RuntimeGroup ^ -gg -g1 ^ -sfrag -sreg ^ -var "var.runtimeFolder" -o runtime.wxs
- 第1引数で収集対象をディレクトリ(dir)と指定します。
- -srdオプションでルートディレクトリ(ここではdirオプションに続けて指定したruntimeディレクトリ)に対応するディレクトリの生成を抑制します。このオプションを指定しないと、インストール先ディレクトリ(C:\Program Files\High Bridge\AnalogClock)の下にruntimeディレクトリが作成され、その下にbinディレクトリ等が配置されます。 -drオプションで、インストール先ディレクトリのIdを指定します。今回はAnalogClock.wxs でAnalogClockプログラムをインストールするディレクトリとして定義したDirectory要素のIdに指定したApplicationFolderを指定しています。
- -cgオプションで、生成するComonentGroupのIdを指定します。このIdは、AnalogClock.wxsのFeature要素の子要素ComponentGroupRefで参照されるので一致させる必要があります。
- -ggオプションで各ComponentのGUIDを生成するよう指定します。
- -ggオプションでGUIDを生成する際、波括弧を省略するよう指定します。
- -sfragオプションでFragment要素をComponent要素毎ではなく、ComponentGroup要素に1つ生成するよう指定します。
- -sregオプションで、DLLファイルに対するレジストリ情報収集(COMのDLLで必要)を抑制するよう指定します。64bit DLLのときにHEAT5150警告が出るのを抑制します。
- -varオプションで、File要素のSource属性で指定するパスの基点となるフォルダを、変数として生成するよう指定します。このオプションを指定しないと、SourceDirという固定文字列がパスの基点となり、存在しないので後のlightコマンド実行時にエラーとなります。
- -oオプションでディレクトリ以下のファイル群を収集した情報を吐き出すWiXファイル名を指定します。
candleコマンド
WiX文書ファイルをcandleコマンドでコンパイルし、wixobjファイル(中間ファイル)を生成します。
D:\work\AnalogClockGadget> candle -arch x64 package\windows\AnalogClock.wxs D:\work\AnalogClockGadget> candle -arch x64 -druntimeFolder=runtime runtime.wxs
- 64bitバイナリをインストールする場合は、-archオプションでx64を指定します。
- -dオプションで、heatコマンドで、File要素のSource属性でファイルパスの基点を変数として生成した箇所に実際のパスを設定します。
実行結果、AnalogClock.wixobj と runtime.wixobjファイルが生成されます。
lightコマンド
Wix中間ファイルであるwixobjファイルから、lightコマンドでインストーラーファイルに生成します。
D:\work\AnalogClockGadget> light AnalogClock.wixobj runtime.wixobj -o analogclock.msi
実行結果、analogclock.msiファイルが生成されます。ちなみに、ファイルサイズは38MBでした。
インストール
この第一歩のインストーラーは、必要最小限の機能しか設定していないので、インストーラーを実行すると対話(ダイアログ)画面もなく、所定のディレクトリにインストールするだけとなります。スタートメニューやデスクトップへのショートカット登録もありません。
analogclock.msiを実行すると、次のように経過を表示する画面が出てすぐに終了します。
プログラムを実行するには、インストール先のC:\Program Files\High Bridge\AnalogClock\bin\analogclock.batを実行します。
インストーラーの作成(第二歩)
第一歩で作成したインストーラーは、所定のディレクトリへプログラムをインストールするだけのものでした。 次は、スタートメニューのプログラム一覧にインストールしたJavaFXアプリケーションを起動するショートカットを追加する機能を盛り込みます。
Directory要素にスタートメニュー(プログラムメニュー)とサブフォルダ定義を追加
<Directory Id="ApplicationFolder" Name="AnalogClock" /> </Directory> </Directory> + <Directory Id="ProgramMenuFolder"> + <Directory Id="ApplicationProgramsFolder" Name="High Bridge" /> + </Directory> </Directory>
Directoryのルート要素の子要素に、Id属性でProgramMenuFolderを指定するDirectory要素を追加します。ProgramMenuFolderは事前定義名で、全ユーザー用のスタートメニューのプログラムディレクトリを示します。(C:\ProgramData\Microsoft\Windows\Start Menu\Programs)
そして、その下にアプリケーションのショートカットを収容するサブフォルダのDirectory要素を追加します。任意のId属性と作成するフォルダ名をName属性に指定します。
ショートカット要素を定義
プログラムを実行するショートカットを定義します。 DirectoryRef要素でショートカットを作成するフォルダを指定し、その子要素にComponent要素で囲ってShortcut要素を定義します。また、アンインストール用のRemoveFolder要素、Shortcut要素のKeyPath代替としてRegistryValue要素を定義します。
<DirectoryRef Id="ApplicationProgramsFolder"> <Component Id="ApplicationShortcut" Guid="*"> <Shortcut Id="ApplicationStartMenuShortcut" Icon="java.exe" Name="Analog Clock Gadget" Show="minimized" Target="[ApplicationFolder]\bin\analogclock.bat" WorkingDirectory="ApplicationFolder"/> <RemoveFolder Id="CleanUpShortcut" Directory="ApplicationProgramsFolder" On="uninstall" /> <RegistryValue Root="HKCU" Key="Software\[Manufacturer]\[ProductName]" Name="installed" Type="integer" Value="1" KeyPath="yes"/> </Component> </DirectoryRef> <Icon Id="java.exe" SourceFile="runtime\bin\java.exe" />
- DirectoryRef要素のId属性は、先に定義したショートカットを収容するフォルダのId属性を参照
- Comonent要素のId属性は、後でFeature要素の子要素に追加するComponentRefで参照する名前を指定
- Shortcut要素
- RemoveFolder要素は、アンインストール時にショートカットフォルダを削除するための定義をしています。
- RegistryValue要素は、Shortcut要素がKeyPath属性を持てない制約を解決するために、レジストリを使用します。
- Icon要素は、Shortcut要素のIcon属性から参照され、ショートカットのアイコンを規定します。Idは対象ファイルと同じ拡張子を持つ必要があります。
Feature要素に追加
<Feature Id="Product" Title="Analog Clock" Level="1">
<ComponentGroupRef Id="RuntimeGroup" />
+ <ComponentRef Id="ApplicationShortcut" />
</Feature>
- Feature要素の子要素に、先ほどショートカット定義で作成したComonent要素のIdを参照するComponentRef要素を追加します。
インストールするとスタートメニューにショートカットが生成
インストーラーの作成(第三歩)
第一歩、第二歩で作成したインストーラーは、インストールを実行すると一瞬ダイアログが表示されますがすぐに消えてインストールが完了してしまいます。
そこで、普通のインストーラーのように、インストール画面を表示し、ユーザーが操作するとインストールが進むようにします。
WiXでは、お仕着せの対話ダイアログがいくつか用意されているので、その中からニーズに合うものを選択することもできますし、カスタムの対話ダイアログを作ることもできます。今回第三歩では、お仕着せの対話ダイアログから最小限の対話を行うWixUI_Minimalを使用します。このWixUI_Minimalは、インストール時にライセンス表示を行い、ユーザーが確認をするとインストールが開始されます。
WiX定義にUIを追加
</Feature> + + <UIRef Id="WixUI_Minimal" /> + <WixVariable Id="WixUILicenseRtf" Value="License.rtf" /> + </Product>
Product要素の子要素にUIRef要素を追加し、Id属性に事前定義済みWixUI_Minimalを指定します。
同じく、Product要素の子要素にWixVariable要素を追加し、Id属性に事前定義済みWixUILicenseRtfを指定、Value属性にRTF(Rich Text Format)形式で用意したライセンス許諾ファイルを指定します。アプリケーションのライセンスを記述したファイルをRTF形式で保存します。RTF形式は、Windows標準ツールのWordpadで作成可能です。
lightコマンドのオプション追加
- light AnalogClock.wixobj runtime.wixobj -o analogclock.msi + light AnalogClock.wixobj runtime.wixobj -o analogclock.msi -ext WixUIExtension
インストール実施
MSI形式のインストーラーファイル(第三歩版)を実行すると、ライセンス画面が表示されます。
ライセンスを承諾すると、インストールが開始します。
インストールが完了すると、インストール完了画面が表示されます。
インストーラーの作成(第四歩)
こ個までの段階では、インストール先が固定となっています。今回は、インストール先を変更するダイアログを追加します。
WiX定義を変更(問題あり)
第三歩で追加した対話ダイアログ種類は、ライセンス表示とインストール結果表示を持つWixUI_Minimalでした。これを、インストール先変更表示を持つWixUI_InstallDirに変更します。 このWixUI_InstallDirを使うときは、アプリケーションをインストールするディレクトリを定義したDirectory要素のIdを、プロパティWIXUI_INSTALLDIRに指定する必要があります。
</Feature> - <UIRef Id="WixUI_Minimal" /> + <Property Id="WIXUI_INSTALLDIR" Value="ApplicationFolder" /> + <UIRef Id="WixUI_InstallDir" /> <WixVariable Id="WixUILicenseRtf" Value="License.rtf" />
インストール実施(問題あり)
インストールを実施すると次の画面が表示されます。
ライセンス表示画面が表示されます。
デフォルトのインストール先が表示されます。
[Change]ボタンを押すと、ディレクトリ選択画面が表示されます。
インストール準備完了の画面が表示されます。
インストールの経過を示す画面が表示されます。
インストールが完了した画面が表示されます。
問題とは?
インストーラーでインストール先を変更したにも関わらず、インストール先はデフォルトのディレクトリとなってしまいました。
いろいろ調べてみたところ、WIXUI_INSTALLDIRプロパティで指定するディレクトリのIdの名前はすべて大文字とする必要があるということが判明しました。
WiX定義を変更(問題解決)
AnalogClock.wxsの変更箇所
<Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFiles64Folder"> <Directory Id="CompanyFolder" Name="High Bridge"> - <Directory Id="ApplicationFolder" Name="AnalogClock" /> + <Directory Id="APPLICATIONFOLDER" Name="AnalogClock" /> </Directory> </Directory>
Show="minimized" - Target="[ApplicationFolder]\bin\analogclock.bat" - WorkingDirectory="ApplicationFolder"/> + Target="[APPLICATIONFOLDER]\bin\analogclock.bat" + WorkingDirectory="APPLICATIONFOLDER"/> <RemoveFolder Id="CleanUpShortcut"
<UIRef Id="WixUI_InstallDir" />
+ <Property Id="WIXUI_INSTALLDIR" Value="APPLICATIONFOLDER" />
<WixVariable Id="WixUILicenseRtf" Value="License.rtf" />
heatコマンド実行オプションの変更
D:\work\AnalogClockGadget> heat dir runtime ^ - -srd -dr ApplicationFolder ^ + -srd -dr APPLICATIONFOLDER ^ -cg RuntimeGroup ^ -gg -g1 ^ -sfrag -sreg ^ -var "var.runtimeFolder" -o runtime.wxs
インストール実施(問題解決)
インストーラーの画面に変更はありませんが、インストール先が無事変更できるようになりました。
jlinkで生成するランチャー(バッチファイル)の修正
jlink で--launcherオプションを指定すると、次のバッチファイルが生成されます。
@echo off set JLINK_VM_OPTIONS= set DIR=%~dp0 "%DIR%\java" %JLINK_VM_OPTIONS% -m com.torutk.gadget.analogclock/com.torutk.gadget.calendar.AnalogClockApp %*
このバッチファイルに2つの修正を加えます。
起動コマンドをjava.exeからjavaw.exeに変更
javaコマンドはコンソール(コマンドプロンプト)を伴うので、このランチャー(バッチファイル)を実行すると画面にコマンドプロンプトが表示されたままとなります。ショートカットでコマンドプロンプトを最小化する設定をしていますが、タスクバーにはコマンドプロンプトが残ったままとなります。
そこで、バッチファイルの中でjavaコマンドをjavawコマンドに修正し、コマンドプロンプトが残らないようにします。
@echo off set JLINK_VM_OPTIONS= set DIR=%~dp0 - "%DIR%\java" %JLINK_VM_OPTIONS% -m com.torutk.gadget.analogclock/com.torutk.gadget.calendar.AnalogClockApp %* + start "" /b "%DIR%\javaw" %JLINK_VM_OPTIONS% -m com.torutk.gadget.analogclock/com.torutk.gadget.calendar.AnalogClockApp %*
- startコマンドでjavawコマンドを起動します。最初の""がないと、startコマンドが"%DIR%javaw" をウィンドウタイトルとして扱い、エラーとなります。
CPU・メモリにやさしいコマンドラインオプションを追加
64bit版のjava(javaw)コマンドはデフォルトではCPU、メモリをがんがん使う設定となっています。 そこで、必要最低限のCPUとメモリ使用にすべく、JavaVMオプションを追加指定します。
@echo off - set JLINK_VM_OPTIONS= + set JLINK_VM_OPTIONS=-Xms32m -Xmx64m -Xss256k ^ + -XX:TieredStopAtLevel=1 -XX:CICompilerCount=2 -XX:CompileThreshold=1500 ^ + -XX:InitialCodeCacheSize=160k -XX:ReservedCodeCacheSize=32m ^ + -XX:MetaspaceSize=12m -XX:+UseSerialGC set DIR=%~dp0 start "" /b "%DIR%\javaw" %JLINK_VM_OPTIONS% ^ -m com.torutk.gadget.analogclock/com.torutk.gadget.analogclock.AnalogClockApp
オプションの意味・値などは次に簡単なまとめ記載 JDK 9 JavaVMオプション - ソフトウェアエンジニアリング - Torutk
まとめ
JavaFXアプリケーションをJDK11&モジュール(JPMS)対応した後に、Windowsインストーラーを作ってJava実行環境を含めて配布できるようにしました。