torutkのブログ

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

Project Jigsaw ことはじめ(その3)

Project Jigsaw ことはじめ(その2) - torutkのブログ の続きです。

前回、2つの依存関係のある次のモジュールから構成されるサンプルプログラムを作成し、依存関係の他、モジュールレベルでの非公開クラスへのアクセスの対応を実験してみました。

  • org.example.bravo
  • org.example.delta

今回は、org.example.deltaモジュールをバージョン番号を変え、複数のバージョンをライブラリにインストールし、どちらが使われるのか実験をします。

org.example.deltaの新しいバージョンを作成

バージョンを1.1としたモジュール定義ファイルに修正します。
src/org.example.delta/module-info.java

module org.example.delta @ 1.1 {
    exports org.example.delta;
}

モジュールのバージョン番号箇所だけ変更(1.0→1.1)しました。

次にソースファイルを修正します。
src/org.example.delta/org/example/delta/impl/HelloDeltaImpl.java

package org.example.delta.impl;

public class HelloDeltaImpl {
    public static String greet() {
	return "Hello Delta 1.1";
    }
}

greetの戻り値となる文字列を修正しました。

コンパイルします。

$ javac -d ./build -modulepath ./build -sourcepath src `find ./src -name "*.java"`
$

新しいバージョンをモジュール・ライブラリにインストールします。
前回の続きですから、現時点は次のモジュールがインストール済みの状態です。

$ jmod ls -L lib
org.example.bravo@1.0
org.example.delta@1.0

org.example.deltaの新しいバージョンをモジュール・ライブラリにインストールします。

$ jmod install -L lib ./build org.example.delta
$ jmod ls -L lib
org.example.bravo@1.0
org.example.delta@1.0
org.example.delta@1.1
$

org.example.bravoが依存するorg.example.deltaが2つモジュール・ライブラリに存在します。どちらが使われるのか、さっそく実行してみます。

$ java -L lib -m org.example.bravo
hello Bravo, Hello Delta 1.1
Warning: CLASSPATH environment variable ignored when -m specified
$

org.example.bravo自体はモジュール・ライブラリに先にインストールしていたまま、変更はしていません。しかし、org.example.deltaの後からインストールしたバージョン1.1を呼び出しています。

org.example.bravoのモジュール定義から、requires文を抜粋します。

    requires org.example.delta;

バージョンを省略しているので、モジュール・リポジトリにあるorg.example.deltaのうち最新バージョンのものを使用します。

ここで、org.example.delta@1.1を削除しようとすると、

$ jmod rm -L lib org.example.delta@1.1
org.openjdk.jigsaw.ConfigurationException: org.example.delta@1.1: being used by org.example.bravo@1.0
  :
$ 

と、使用しているモジュールがあるためエラーとなります。

では、古いバージョンであるorg.example.delta@1.0を削除しようとすると

$ jmod rm -L lib org.example.delta@1.0

$ jmod ls -L lib
org.example.bravo@1.0
org.example.delta@1.1

と問題なく削除されました。

org.example.bravoが依存するorg.example.deltaのバージョン指定

org.example.deltaのバージョンを更新していくといずれorg.example.bravoと整合がとれなくなります。そこで、最新版ではなく、整合の取れるバージョンを指定してみることにしました。

src/org.example.bravo/module-info.java

module org.example.bravo @ 1.0 {
    requires org.example.delta @ < 2.0;
    class org.example.bravo.HelloBravo;
}

バージョン番号として、1.xを許容したいので、< 2.0を指定しました。

org.example.bravoをコンパイルします。

$ javac -d ./build -modulepath ./build -sourcepath src `find ./src -name "*.java"`

モジュール・ライブラリの内容を確認します。

$ jmod ls -L lib
org.example.bravo@1.0
org.example.delta@1.1
$ 

org.example.bravoをいったんモジュール・ライブラリから削除します。

$ jmod rm -L lib org.example.bravo@1.0

org.example.bravoをモジュール・ライブラリにインストールします。

$ jmod install -L lib ./build org.example.bravo
$ java -L lib -m org.example.bravo
hello Bravo, Hello Delta 1.1
Warning: CLASSPATH environment variable ignored when -m specified

org.example.deltaのバージョンアップ

org.example.deltaに、互換性のないAPI変更を伴う修正を入れます。

src/org.example.delta/module-info.java

module org.example.delta @ 2.0 {
    exports org.example.delta;
}

バージョンを2.0にしました。

src/org.example.delta/HelloDelta.java

package org.example.delta;

import org.example.delta.impl.HelloDeltaImpl;

public class HelloDelta {
    public static String getGreeting() {
	return HelloDeltaImpl.greet();
    }
}

org.example.bravoから呼ばれるAPIを互換性のない変更をしています。

  • (修正前)public static String greet()
  • (修正後)public static String getGreeting()

org.example.deltaをコンパイルします。

$ javac -d ./build -modulepath ./build -sourcepath src `find ./src/org.example.delta -name "*.java"`

org.example.deltaをインストールします。

$ jmod install -L lib build org.example.delta
$ jmod ls -L lib
org.example.bravo@1.0
org.example.delta@1.1
org.example.delta@2.0
$

org.example.bravoを実行します。

$ java -L lib -m org.example.bravo
hello Bravo, Hello Delta 1.1
Warning: CLASSPATH environment variable ignored when -m specified

バージョン番号を指定していたので、エラーになることなく実行できました。

(詳細)Jigsaw モジュールのバージョン番号

バージョン番号の記述方法については、次のURLにBNF風記法の記載があります。

これによると、requires文のモジュール名とバージョン番号の指定の書式は

ModuleNameAndVersionQuery:
   ModuleName [@ VersionQuery]

VersionQuery:
  [Operator] VersionLiteral

VersionLiteral:
  JavaDigit VersionLiteralChars

VersionLiteralChars:
  VersionLiteralChar
  VersionLiteralChars VersionLiteralChar

VersionLiteralChar:
  JavaLetterOrDigit
  .
  -

とあります。バージョン番号の文字列は、先頭が数字(JavaDigit)で、以降は文字・数字、ピリオド、ハイフンが続くものです。1.2.3-1 や、2.3-preview などは合致します。

OperatorについてはBNF風定義には見当たりませんでした。本文中にある記載は、

The operator in a version query must be <<, <=, =, >=, or >>, or a compile-time error occurs.

とあります。ただし、<< を指定したらエラーとなり、 < を指定することで回避できます。