注)Jigsawは、Java SE 8には間に合わず、その後も仕様検討・開発が継続され、Java SE 9に導入されることになりました。すでに本記事は完全に古い内容となっています。
Java SE 8で導入予定のモジュールシステムが、Project Jigsawです。JavaでJigsawという名称を聞くとw3cのHTTPサーバーを連想してしまう人もいるかと思いますが*1、それとは無関係のモジュール管理の仕組みです。ここでいうモジュールとはJARファイルの類です。
JavaOneなどでこのProject Jigsawの解説はされているのですが、目で見ているだけではピンと来なく、実際に手で動かしてみないとと思っていたら、先月にJigsawのプレビュー版が公開されました。
以下からJigsaw機能入りJDK8プレビュー版*2がダウンロードできます。
ダウンロードの種類に、OSとは別に
と2種類が選べます。
とりあえず、自分で作成するプログラムをProject Jigsawの機能でモジュール化したい、というときは前者を、JDKの機能を取捨選択して組み合わせて実行してみたい、というときは後者を入手して動かすとよいかと思います。
ここでは、前者を使って簡単なプログラム(伝統にのっとりHello world)をProject Jigsawの機能でモジュール化して実行するところまでを試してみます。
JDK 1.8 Jigsaw Previewのインストール(Windows)
上述のURLから、"JDK module image"のzipファイルをダウンロードします。本日時点でダウンロードしたファイル名(Windows 64bit版)は次のとおりです。
任意の場所に展開します。
試した例では、C:\Program Files\Java\の下に展開し、ディレクトリ名をjdk-module-imageからjdk1.8.0_Jigsawに変更しました。
続いて、環境変数PATHをC:\Program Files\Java\jdk1.8.0_Jigsaw\binに通しておきます。
プログラムを作る
モジュール名の決定
まず、モジュール構成を決めます。今回作るHelloプログラムは1つのモジュールとします。モジュール名は通常代表パッケージ名に合わせます。今回は、パッケージ名を
package org.example.alfa;
とし、よってモジュール名もorg.example.alfaとします。
ソースディレクトリ構成の作成
ソースファイルのディレクトリは、今までのやり方では次のようになります(一例)。
1stJigsaw +-- src +-- org +-- example +-- alfa +-- HelloAlfa.java
Jigsawでは、モジュール毎にディレクトリを分けるので、
1stJigsaw +-- src +-- org.example.alfa +-- module-info.java +-- org +-- example +-- alfa +-- HelloAlfa.java
のように、ソースディレクトリの基点の下にモジュール名のディレクトリを設け、その下にmodule-info.javaというモジュール情報を定義するファイルと、従来どおりのパッケージ名にちなんだディレクトリを用意します。
Helloプログラム
エントリポイントとなるmainメソッドを持つクラス(HelloAlfa)を作成します。
package org.example.alfa; public class HelloAlfa { public static String greet() { return "Hello, Alfa"; } public static void main(String[] args) { System.out.println(HelloAlfa.greet()); } }
モジュール定義ファイル
モジュールを定義するため、module-info.javaというファイル名規定のソースファイルを作成します。なお、このファイルはパッケージのルートにあたるディレクトリに置く必要があります。
ということで、複数のモジュールを1つのパッケージツリー上に作ることができないため、モジュール毎にディレクトリを分けるという構成になるのだと思います。
module org.example.alfa @ 1.0 { class org.example.alfa.HelloAlfa; }
- モジュール名org.example.alfa
- モジュールのバージョンを1.0
- 実行可能なモジュールとして定義し、エントリポイント(mainメソッド)を持つクラスorg.example.alfa.HelloAlfaを指定
という内容です。バージョンは省略できます。省略した場合は次のようになります。
module org.example.alfa {
class org.example.alfa.HelloAlfa;
}
- 注)現在バージョン番号を省略したモジュールでエントリポイントを指定しているものはインストール時にエラーとなるようです。
ビルドする
ビルドの流れは
- コンパイルする(.javaファイルから.classファイルを生成する)
- モジュールリポジトリを作成する
- モジュールリポジトリにモジュールを追加する
- (オプション)再配布用のモジュールファイルを作成する
となります。
コンパイルした場所で実行するならオプションは不要です。
コンパイルする
シェル環境(bash)では次のように実行します。
- 2012/6/3追記:以下のjavacのコマンドラインオプションの使い方は間違っている可能性があります。Project Jigsawのサイトにjavacのコンパイルオプションについての説明が記載された資料(以下URL)があるので、確認後必要があれば修正します。
~$ cd 1stJigsaw 1stJigsaw$ ls src 1stJigsaw$ mkdir build 1stJigsaw$ ls build src 1stJigsaw$ javac -d ./build -modulepath ./build -sourcepath src `find ./src -name "*.java"` 1stJigsaw$
Windowsのコマンドプロンプトでは次のように実行します。
C:\Users\torutk> cd 1stJigsaw C:\Users\torutk\1stJigsaw> dir /b src C:\Users\torutk\1stJigsaw> mkdir build C:\Users\torutk\1stJigsaw> dir /b build src C:\Users\torutk\1stJigsaw> dir /s /b src\*.java > sources.txt C:\Users\torutk\1stJigsaw> javac -d .\build -modulepath .\build -sourcepath .\src @sources.txt C:\Users\torutk\1stJigsaw>
この結果コンパイルされた結果は次のようになります。
1stJigsaw +-- build +-- org.example.alfa +-- module-info.class +-- org +-- example +-- alfa +-- HelloAlfa.java
モジュールリポジトリを作成する
モジュールを収める場所(リポジトリ)を作成します。
1stJigsaw> jmod create -L lib 1stJigsaw> ls build lib src 1stJigsaw>
これでできました。libのなかには正体不明の管理ファイルが生成されています。
1stJigsaw> ls lib %jigsaw-library %lock %mids 1stJigsaw>
モジュールリポジトリにモジュールを追加する
先ほどコンパイルしたorg.example.alfaモジュールをこのリポジトリに追加します。
1stJigsaw> jmod install ./build org.example.alfa -L lib 1stJigsaw>
モジュールリポジトリの下は次のようになります。
1stJigsaw : +-- lib +-- %jigsaw-library +-- %lock +-- %mids +-- org.example.alfa +-- 1.0 +-- classes +-- config +-- index +-- info
メモ
(オプション)再配布用のモジュールファイルを作成する
プログラムを配布するためにモジュールファイルを作成します。モジュールファイルは、jpkgコマンドで作成します。モジュールファイルのソースは、モジュールリポジトリではなく、コンパイル結果を生成したディレクトリとなります。
1stJigsaw> jpkg -m ./build/org.example.alfa jmod org.example.alfa 1stJigsaw> ls build lib org.example.alfa@1.0.jmod src 1stJigsaw>
jpkgでモジュールリポジトリからモジュールファイルを生成できた方がいいと思いましたが、そういう思想ではない模様です。jpkgで-mオプションではなく-Lオプションでモジュールリポジトリを指定してモジュールファイルを生成しようとしたらエラーとなってしまいました。
モジュールの実行
モジュールリポジトリにインストールしたモジュールを実行します。
1stJigsaw> java -L lib -m org.example.alfa Warning: CLASSPATH environment variable ignored when -m specified Hello, Alfa 1stJigsaw>
なお、ビルド結果のディレクトリから直接実行する方法(javaコマンドの-modulepathオプション)はエラーとなってしまいました。Preview版だからかもしれません。
1stJigsaw> java -modulepath build -m org.example.alfa Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit. Unrecognized option: -modulepath
モジュールファイルのインストール
モジュールファイルを受け取ったら、モジュールリポジトリにインストールして実行します。
1stJigsaw> jmod install org.example.alfa@1.0.jmod -L lib : (既に存在しているモジュールをinstallしようとするとエラーになる)
同じモジュール(+バージョン)を再度インストールしようとするとエラーとなります。現時点のプレビュー版*3では回避方法がありません。
参考になる情報
そこで、いったんモジュールリポジトリを丸ごと削除し、インストールします。
1stJigsaw> rm -r lib 1stJigsaw> jmod create -L lib 1stJigsaw>
モジュールファイルからインストールします。
1stJigsaw> jmod install org.example.alfa@1.0.jmod -L lib 1stJigsaw>
Project Jigsawで何が変わるのか
今、Javaでライブラリを開発して公開するときは、JARファイルの形式で提供しています。バージョンは、ファイル名に付けるか(foo-1.2.3.jar)、JARファイル内のマニフェストファイルに記述するか(MANIFEST.MFのSpecification-VersionやImplementation-Version)でした。JARファイル内のマニフェストは任意なので、記述のないライブラリが大半です。また記述があっても読み取る手段が乏しく活用されることはあまりありません。
複数のJARファイルを組み合わせるときに、不足するJARがあったとき、JARのバージョンが不整合で不足するクラス・メソッドがあったとき、どちらも実際に実行してからのエラーとなります。
Javaのpackageのアクセス制御では、ライブラリ外には公開したくないクラスであってもライブラリ内で複数のpackageから利用する場合publicにせざるを得ず、publicとしたらライブラリ外からも見えてしまいます。
このようなJARの管理の課題を解決し、もっと今流の管理ができるように改善したのがProject Jigsawです。モジュールという仕組みを導入することで、プログラムの管理を容易にするのですが、今まで実行時のファイル構成など考えなかった人たちには、プログラム開発時にモジュールに合わせたディレクトリ構成を取る必要があるので、覚えておくべき項目が増えて厄介に感じるかもしれません。
参考文献
JavaOne Tokyo 2012のセッション資料
Project Jigsawのドキュメント"Jigsaw Quick Start"
さいごに
今回は、Helloレベルの最低限のプログラムを実行するための手順を確認しました。
次は、複数のモジュールを扱うことや、隠蔽するパッケージ、バージョンアップといったことに取り組む予定です。(時期未定)
*1:http://www.w3.org/Jigsaw/ いや、ほとんどいないでしょうけれど
*2:ラムダは含まれてません
*3:2012/6/22のbuild 1.8.0-ea-jigsaw-nightly-h458-20120622-b42-b00ではjmod removeが追加され、モジュールを削除する手段が提供されるようになりました。