この記事は、Java Advent Calendar 2015 の 13 日目の記事です。
昨日は @cero_t さんのStream APIをつくろう でした。明日は opengl_8080 さんです。
JavaOne にいってから、Project Jigsaw で遊ぶことが多くなりました。で、モジュールを作った後の話を紹介します。ちょうど、JavaFX in the Box の方の このエントリー の後の話題のようなものです。
このエントリーでは JavaFX のサンプルのモジュールの依存性を調べたのですが、せっかく依存性を調べたのですから、モジュールを作ってみましょう。
サンプルはこれです。
package fxdemo; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.layout.StackPane; import javafx.scene.text.Font; import javafx.stage.Stage; public class FXDemo extends Application { @Override public void start(Stage stage) throws Exception { Label label = new Label("Label"); label.setFont(Font.font(24)); StackPane root = new StackPane(label); Scene scene = new Scene(root); stage.setScene(scene); stage.setTitle("FXDemo"); stage.show(); } public static void main(String... args) { launch(args); } }
このサンプルが依存しているのは、java.base モジュール、javafx.controls モジュール、そして javafx.graphics モジュールです。ですので、module-info.java は次のようにしました。
module fxdemo { requires javafx.controls; requires javafx.graphics; }
では、コンパイルして、モジュールを作ってみましょう。ソースは src ディレクトリ、クラスは bin ディレクトリ、モジュールは mods ディレクトリに置くとしましょう。
C:\fxdemo>javac -d bin src\module-info.java src\fxdemo\FXDemo.java C:\fxdemo>jar --create --file mods\fxdemo.jar --module-version 1.0 -C bin .
これで、モジュールができました。JAR ファイルなので、これだけだとモジュールかどうかよく分からないのが玉にキズ。
では、実行してみましょう。
C:\fxdemo>java -mp mods -m fxdemo/fxdemo.FXDemo Exception in Application constructor Exception in thread "main" java.lang.RuntimeException: Unable to construct Appli cation instance: class fxdemo.FXDemo at com.sun.javafx.application.LauncherImpl.launchApplication1(javafx.gra phics@9-ea/LauncherImpl.java:926) at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$138( javafx.graphics@9-ea/LauncherImpl.java:220) at java.lang.Thread.run(java.base@9-ea/Thread.java:747) Caused by: java.lang.IllegalAccessException: class com.sun.javafx.application.La uncherImpl (in module javafx.graphics) cannot access class fxdemo.FXDemo (in mod ule fxdemo) because module fxdemo does not export fxdemo to module javafx.graphi cs at sun.reflect.Reflection.throwIllegalAccessException(java.base@9-ea/Ref lection.java:452) at sun.reflect.Reflection.ensureMemberAccess(java.base@9-ea/Reflection.j ava:135) at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(java.base@9- ea/AccessibleObject.java:370) at java.lang.reflect.AccessibleObject.checkAccess(java.base@9-ea/Accessi bleObject.java:362) at java.lang.reflect.Constructor.newInstance(java.base@9-ea/Constructor. java:435) at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$144 (javafx.graphics@9-ea/LauncherImpl.java:838) at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$158(javafx. graphics@9-ea/PlatformImpl.java:351) at com.sun.javafx.application.PlatformImpl.lambda$null$156(javafx.graphi cs@9-ea/PlatformImpl.java:320) at java.security.AccessController.doPrivileged(java.base@9-ea/Native Met hod) at com.sun.javafx.application.PlatformImpl.lambda$runLater$157(javafx.gr aphics@9-ea/PlatformImpl.java:319) at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(javafx.graphics@9-e a/InvokeLaterDispatcher.java:96) at com.sun.glass.ui.win.WinApplication._runLoop(javafx.graphics@9-ea/Nat ive Method) at com.sun.glass.ui.win.WinApplication.lambda$null$130(javafx.graphics@9 -ea/WinApplication.java:191) ... 1 more C:\fxdemo>
あれ、動かない。
まぁ、理由は簡単で、module-info.java に exports の項を書かなかったためです。このサンプルは外部から使うわけではないと思ったわけですが、実行するということは main メソッドを外部から呼ぶことになるため、exports が書いてないと実行できないのです。
ということで、module-info.java を次のように書きかえました。
module fxdemo { requires javafx.controls; requires javafx.graphics; exports fxdemo; }
これで、同じようにコンパイルして、モジュールを作ったら、無事に実行できました。
Jigsaw で実行する場合は、-modulepath もしくは -mp でモジュールがおいてあるディレクトリを指定し、-m でメインクラスを指定します。この時、[モジュール名]/[クラス名] のようにモジュールとクラス名を / で区切って併記するようにします。
さて、これでモジュールができたので、次にこのサンプルのモジュールと最小限のモジュールを含む JRE を作ってみましょう。
それをやるには jlink コマンドを使用します。
C:\fxdemo>jlink --modulepath mods;"c:\Program Files\Java\jdk-9\jmods" --addmods fxdemo --output fxdemo
オプションはだいたい分かると思いますが、--modulepath でモジュールのディレクトリを指定します。サンプルのモジュールだけでなく、JDK のモジュールの場所も指定しておきます。--addmods が追加するモジュールです。javafx.controls モジュールなどを追加しないのは、依存性の記述から勝手にやってくれるからです。
そして、fxdemo ディレクトリにイメージを作成します。このディレクトリには bin、conf、lib のディレクトリを作成します。
bin ディレクトリには java コマンドがあるので、どういうモジュールがあるか調べてみましょう。
C:\fxdemo\fxdemo\bin>java -listmods fxdemo@1.0 java.base@9-ea java.datatransfer@9-ea java.desktop@9-ea java.instrument@9-ea java.logging@9-ea java.management@9-ea java.naming@9-ea java.prefs@9-ea java.rmi@9-ea java.security.sasl@9-ea java.xml@9-ea javafx.base@9-ea javafx.controls@9-ea javafx.graphics@9-ea jdk.jfr@9-ea jdk.vm.ci@9-ea
javafx.controls モジュールなどの依存性も解決することで、必要最低限のモジュールを導入した JRE を作成することができました!