盲目的に --add-modules の JVM オプションで解消するのはやめて、Java 11 に備えてちゃんとした対処をしてみよう、というお話です。

Java 9 のモジュールシステムと deprecated なモジュールによる問題

Java 9 ではモジュールシステムの導入と同時に、Java の標準クラスライブラリを構成するモジュールのうち Java 11 で削除される予定のモジュール (JEP 320) が deprecated となりました。具体的には同 JEP に記載されているとおり、以下の 6 つのモジュールが削除予定となっています。

これら deprecated となったモジュールは JDK 9 のデフォルトでは解決されないようになっているため、同モジュールのクラスを利用している Java のコード、例えば以下のようなコードはそもそも javac でコンパイルが通りません。1

/* 
 * このコードを JDK 9 の javac でコンパイルしようとすると、以下のようなエラーになる。
 * 
 * HelloJava9.java:11: エラー: パッケージjavax.xml.bindは表示不可です
 * import javax.xml.bind.JAXBException;
 *                 ^
 *   (パッケージjavax.xml.bindはモジュールjava.xml.bindで宣言されていますが、モジュール・グラフにありません)
 * エラー1個
 */

import javax.xml.bind.JAXBException;

public class HelloJava9 {
    public static void main(String[] args) {
        new JAXBException("このコードは javac だとコンパイルできない")
                .printStackTrace();
    }
}

また JDK 8 以前でコンパイル済みのコードを JDK 9 の java コマンドで実行しようとしても、以下のような ClassNotFoundExceptionNoClassDefFoundError が発生して実行できません。

Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
	at HelloJava9.main(HelloJava9.java:15)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
	... 1 more

Deprecated なモジュール由来の例外・エラーへの対処方法

これらの deprecated なモジュールを利用していることによる例外・エラーに対処する方法として、よく挙げられるのが java コマンドにて --add-modules オプションを指定する方法でしょう。実際にこの方法について言及しているブログ記事や Stack Overflow の回答は、国内外を問わずちらほらと見受けられます。

確かに --add-modules javax.xml.bind と実行時にオプションを指定するだけで解決できるのであれば、Java アプリケーションのビルドをごにょごにょするといった面倒なことも必要もないので、特に Eclipse などすでにビルド済みの Java アプリケーションを Java 9 で動かしたい場合などには有効な解決策となることでしょう。しかし Oracle による JDK 9 への移行ガイド では、deprecated なモジュールを --add-modules で指定することについて

これらのモジュールは最終的にはJDKから削除される予定のため、これは一時的な回避策です。

と言及しており、この対応方法が恒久的に利用できうる方法でないことは明らかです。ではどうすればいいのかと言うと、同移行ガイドに

クラス・パスにあるスタンドアロン・バージョンのAPI(および必要に応じて実装)をデプロイしてください。各Java EE APIは、Maven Centralで公開されているプロジェクトによるスタンドアロンのテクノロジです。

と書かれているとおり、Maven / Gradle / SBT などお使いのビルドツールにて Maven Central 上のアーティファクトを利用するよう依存ライブラリを追加するのが適切な方法と言えるでしょう。

Deprecated な各モジュールに対応するアーティファクト

Deprecated なモジュールは Maven Central 上のアーティファクトで置き換える、という方針で対処するとして、では具体的にどのアーティファクトを利用すればいいのでしょうか?

先に示した JEP 320 では、それぞれのモジュールに関連する Mavel Central 上のアーティファクトをいくつか示しています。しかし、具体的にどのアーティファクトを依存ライブラリとして追加すればよいのかまでは明記されていません。

ここでは、Stack Overflow における “Replacements for deprecated JPMS modules with Java EE APIs” への回答 を参考に、どのアーティファクトを利用 (依存ライブラリとして追加) すべきかをモジュールごとに明らかにしていきます。

java.xml.ws

JAX-WS にはリファレンス実装 com.sun.xml.ws : jaxws-ri : 2.3.0.1 が存在しており、このアーティファクトで置き換えられます。

java.xml.bind

この JAXB もリファレンス実装が存在しています。

依存ライブラリとして追加すべき具体的なアーティファクトは、Stack Overflow の回答にあるようにまず次の 3 つになります。

加えて Stack Overflow の別の回答 にあるように、JAXB (jaxb-impl) は java.activation のモジュールに依存していることにより、別途 JAF のアーティファクトも依存に追加する必要があります (次の java.activation を参照)。

java.activation

JEP 320 では、API のみを含む JAF のアーティファクトとして javax.activation : javax.activation-api : 1.2.0 が挙げられています。

一方で Stack Overflow では com.sun.activation のパッケージの実装を含んだ com.sun.activation : javax.activation : 1.2.0 を挙げています。

両者の違いは、単に com.sun.activation パッケージのクラスが存在するかしないかだけのようです。

java.xml.ws.annotation

こちらも API のみのアーティファクト javax.annotation : javax.annotation-api : 1.3.2 が存在し、これで置き換えられます。

java.corba

CORBA については他のモジュールとは事情が異なり、JEP 320 にあるように

There will not be a standalone version of CORBA unless third parties take over maintenance of the CORBA APIs, ORB implementation, CosNaming provider, etc.

というわけでスタンドアロンで利用できるアーティファクトは存在しないようです 😢

java.transaction

JTA はアーティファクト javax.transaction : javax.transaction-api : 1.3 が存在します。

なお 1.3 以前のバージョンの同アーティファクトには javax.transaction.xa パッケージの実装が含まれていることに注意が必要です。 JDK 9 では、javax.transaction.xa パッケージの実装は java.sql モジュール に含まれているため、1.3 以前のアーティファクトと java.sql モジュールを同時に利用したり、もしくは (こんなことやることはないと思うけど) --add-modules java.transaction とした場合にはよからぬ問題が生じる可能性があります。

まとめ

  • CORBA (java.corba モジュール) を除いた 他のモジュールは、代替となるアーティファクトが Maven Central 上に存在しているよ
  • モジュールによっては、複数のアーティファクトを依存に追加する必要があったりバージョンに気を使う必要があるよ
  • 何も考えずに --add-modules するよりも、いずれ訪れる Java 11 に向けて少しずつ準備を進められるといいね

  1. ターゲットバージョンを 1.8 以下にしていると、JDK 9 を使っていていもコンパイルは通ります。IntelliJ IDEA や Gradle などを使っている場合、利用しようとしている JDK のバージョンとプロジェクトで設定されているターゲットバージョンが異なっていて問題の発覚が遅れることもあります。 

Tags:

Categories:

Updated: