torutkのブログ

ソフトウェア・エンジニアのブログ

JDK 9 EA上でNetBeans IDE 開発版を動かす(動いた)

id:torutk:20160722で、JDK 9 EA上でNetBeans IDE開発版はまだ動いていないと書いてから1年近くが過ぎました。もうすぐJDK 9もリリース(今年9月予定)ということもあり、そろそろ動く頃かと確認してみました。

動作環境

OS:Windows 10 1607 64bit
JDK: Java SE 9 Development Kit, Early Access b174 64bit
NetBeans IDE: NetBeans IDE dev 201706160001

インストール

最初にJDK 9を入れておきます。
NetBeans IDE devをインストールするときに、使用するJDKの選択でJDK 9が指定できるようになります。
前のバージョンの設定を引き継ぐとトラブルの元なので、前のバージョンの設定を引き継ぐかインストーラーから聞かれたら引き継がないを指定します。

トラブルメモ

インストール後、起動すると多数のモジュールが見つからないといったエラーが表示されるマシンがありました。同じバージョンを入れた別のマシンでは動いていたので、一度アンインストールし、ユーザーの作業フォルダ・キャッシュフォルダを削除して、再度インストールたところ起動するようになりました。

プラグインのインストール

プラグインポータルで公開されているプラグインは、開発版ではデフォルトで配布サイトが設定されていないので、NetBeans IDE 8.2のプラグインポータルサイトを設定します。

http://plugins.netbeans.org/nbpluginportal/updates/8.2/catalog.xml.gz

次の2つをインストールしました。

なお、Darcula LAF foor NetBeansを入れると、NetBeans起動時にエラーダイアログが表示されますが、[取消]ボタンを押して続行することができます。

Jigsaw(モジュール)の確認

Java SE 9 では、(仕様の承認が遅れていますが)モジュール仕様(開発コード:Jigsaw)が導入されます。そこで、モジュール仕様の確認をしてみます。

まず、新規プロジェクトで、JavaFX FXMLアプリケーション を選択してJavaFXアプリケーションの雛形を生成します。

とりあえず、実行して、問題なくビルド&実行できることを確認します。
次に、新規ファイルで、Java Module Info を選択します。

選択直後のプロジェクトビューとmodule-info.javaのコードは次の画面になります。

module-info.javaを追加すると、依存関係を定義していないモジュールのクラスを使用しているソースファイルがエラーとして表示されます。

エラー行にカーソルを持っていくと、次のようにエラー内容が表示されます。

エラーに従って、必要なモジュールをmodule-info.javaに記述していきます。
キーワード(requiresやexports)が識別され、モジュール名も補完候補が表示され、楽に入力できるようになっています。

module-info.javaに、javafx.grahicsモジュールを記述すると、次のようにエラーが解消されます。

  • module-info.javaを保存する必要があります。

同様に、module-info.javaに、javafx.fxmlモジュール、javafx.controlsモジュールを記述していきます。

module com.torutk.hello {
    exports com.torutk.hello;
    requires javafx.controls;
    requires javafx.fxml;    
    requires javafx.graphics;
}

ビルドしたJARファイルを、コマンドラインからモジュール指定して実行してみます。

HelloJavaFx>java --module-path dist -m com.torutk.hello/com.torutk.hello.HelloApp
Exception in Application start method
java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   :
Caused by: javafx.fxml.LoadException:
file:///D:/work/java/NetBeansProjects/jigsaw/HelloJavaFx/dist/HelloJavaFx.jar!/com/torutk/hello/HelloView.fxml:11

        at javafx.fxml/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2625)
   :
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private javafx.scene.control.Label com.torutk.hello.HelloViewController.label accessible: module com.torutk.hello does not "opens com.torutk.hello" to module javafx.fxml
        at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
   : 

実行できましたが、実行時例外が発生してしまいました。
FXMLLoader.load からの呼び出して、privateフィールドへのアクセスが拒否されたためのようです。

javafx.fxmlモジュールから、com.torutk.helloモジュールのprivate要素にリフレクションでアクセスするには、追加設定が必要なようです。

ざっと調べまわったところ

JavaOne 2016時点での情報

  • module-info.java に、exports private <モジュール名> と記述

コンパイルすると、エラーとなってしまいます。

src\module-info.java:6: エラー: <identifier>がありません
    exports private com.torutk.hello;
           ^
src\module-info.java:6: エラー: パッケージは空であるか、または存在しません <error>
    exports private com.torutk.hello;
            ^

JavaDay Tokyo 2017時点での情報
Java Day Tokyo 2017のセッション資料 Modular Development with JDK 9

  • module-info.java に、open <モジュール名> (to <モジュール名>)* と記述
    opens com.torutk.hello to javafx.fxml;

ということで、module-info.java を次のように記述すると実行できるようになりました。

module com.torutk.hello {
    requires javafx.controls;
    requires javafx.fxml;    
    requires javafx.graphics;
    
    exports com.torutk.hello;
    opens com.torutk.hello to javafx.fxml;
}

記述量を減らすために、次のように記述してもよいようです。

open module com.torutk.hello {
    requires javafx.controls;
    requires javafx.fxml;    
    requires javafx.graphics;
}

exportsとopensの指定を、モジュール宣言部にopenとしてしまうことで省略できるのですね。
ただし、これだとどのモジュールへもopenになってしまいます。

ネイティブインストーラーの作成

ここまで来たら、ネイティブインストーラー(WindowsMSI形式など)を作って、バンドルされるJREがどれだけ小さくなるか確認したくなります。JDK 9のjavapackagerコマンドで、Windows MSI形式インストーラーを作成します。

> javapackager -deploy -native msi -outdir dist -outfile HelloJavaFx ^
--module-path dist --module com.torutk.hello/com.torutk.hello.HelloApp ^
-vendor Takahashi

vendor を指定しなかったときは、OSのログイン名(サインイン名)が使われますが、ASCII文字以外のサインイン名だとエラーになってしまいます。明示的にvendorオプションを指定しておきます。

モジュール化(Jigsaw化)されたアプリケーションの場合は、javapackagerのオプションで、従来の-srcdirオプション、-srcfilesオプションに変えて--module-pathオプション、--moduleオプションを指定します。-appclassオプションでMainクラスを指定していたものは、--moduleオプションでモジュール名 + "/" の続きにメインクラス名を記述すればよいようです。

種類 MSIファイルサイズ インストールディレクトリサイズ
JDK 9モジュール対応 31MB 90MB
JDK 9モジュール非対応 59MB 167MB

モジュール化によって、依存するJREのモジュールだけがバンドルされるようになるので、インストーラーファイルのサイズと実際にインストールされたディレクトリ以下のファイルサイズがそれぞれほぼ半分になりました。

その他

module-info.javaの可視化表示

module-info.javaNetBeans IDEで開くと、ソースコード表示の他、グラフ表示が選択できます。