#はじめに
特に要望はありませんでしたが、WildFlyをワイルドに動かしてみるの続きの記事を書きます。今回はクラスローディングについてです。今回は2018年12月時点で最新の WildFly-15.0.0.Final
を使用します。
#WildFly のクラスローディング
WildFly に含まれる jar はモジュールという考え方で管理されています。モジュールには静的モジュールと動的モジュールの2種類があります。
##モジュールの種類
1.静的モジュール
modules ディレクトリ配下にインストールされた jar を指します。ユーザーが作成したカスタムモジュールの jar を置くこともできます。
2.動的モジュール
war や ear ファイル内にある jar やクラスを指します。
アプリケーションから静的モジュールを使用する場合、アプリケーション内にデプロイメント記述子ファイルjboss-deployment-structure.xml
を置き、このモジュールを使いますよという宣言である依存関係を記述する必要があります。
ただし、静的モジュールの中には暗黙的な依存関係を持つモジュール1があり、これらはjboss-deployment-structure.xml
に記述しなくても勝手にロードされます。
##クラスローディングの優先順位
クラス(モジュール)のロードには優先順があり、同じパッケージの同名クラスがあった場合は先にロードされた優先順位が高いものが使用されます。
各モジュールは以下の順番2でロードされます。
- 暗黙的依存関係の静的モジュール1(System Dependencies)
- ユーザーが
jboss-deployment-structure.xml
に設定した静的モジュール(User Dependencies) - 動的モジュール(Local Resource)
##本当かどうか確認してみる
疑り深いので本当かどうか試してみます。
###確認用JSPの作成
暗黙的依存関係のモジュールの中のorg.apache.log4j
を使って確認します。まず下記のような jsp を用意します。
<%@page contentType="text/html; charset=UTF-8" %>
<html>
<body>
<%
Object logger = org.apache.log4j.Logger.getLogger("test");
out.print(logger.getClass().getName());
out.print("<br>");
out.print(logger.toString());
%>
</body>
</html>
###確認用にせクラスの作成
以下のような偽の Logger クラスを作成、コンパイルして classloadTestInModules.jar に固めます。これを静的モジュールとして modules ディレクトリ配下にインストールします。
package org.apache.log4j;
public class Logger {
public static String getLogger(String name) {
//org.apache.log4j.Loggerクラスの替わりに文字列moduleを返す。
return "module";
}
}
次に少しだけ変更して、同様にコンパイルして classloadTestInWar.jar に固めます。これは動的モジュールとしてアプリケーション(war)に含めます。
package org.apache.log4j;
public class Logger {
public static String getLogger(String name) {
//org.apache.log4j.Loggerクラスの替わりに文字列warを返す。
return "war";
}
}
###静的モジュールのインストール
作成した classloadTestInModules.jar を modules ディレクトリ以下にインストールします。
以下のディレクトリを作成します。
wildfly-15.0.0.Final/modules/system/layers/base/test
wildfly-15.0.0.Final/modules/system/layers/base/test/test
wildfly-15.0.0.Final/modules/system/layers/base/test/test/main
下記に classloadTestInModules.jar を置きます。
wildfly-15.0.0.Final/modules/system/layers/base/test/test/main
module.xml
を以下の内容で作成します。test.test
がこのモジュールの名前になります。
<?xml version="1.0" encoding="UTF-8"?>
<module name="test.test" xmlns="urn:jboss:module:1.7">
<resources>
<resource-root path="classloadTestInModules.jar"/>
</resources>
</module>
次に wildfly-15.0.0.Final/standalone/deployments
にある適当なアプリケーション helloworld.war
の WEB-INF/lib
に classloadTestInWar.jar
war 直下に logger.jsp
を置きます。デプロイメントマーカーファイル helloworld.war.dodeploy
を作成してデプロイさせて準備完了です。
###暗黙的依存関係のモジュールが使用される?
ブラウザでhttp://localhost:8080/helloworld/logger.jsp
にアクセスします。
以下のように表示されました。ちゃんと Logger クラスのオブジェクトが戻ってきていますので、暗黙的依存関係のモジュールのクラスが返ってきたことが分かります。
org.apache.log4j.Logger
org.apache.log4j.Logger@62af7a58
想定通りに動きました
###ユーザーが置いた静的モジュールが使用される?
暗黙的依存関係のモジュールではなくユーザーが指定したモジュールを使いたいという設定を入れてみます。helloworld.war/META-INF
にデプロイメント記述子ファイル jboss-deployment-structure.xml
を以下の内容で作成します。
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
<deployment>
<exclusions>
<module name="org.apache.log4j" />
</exclusions>
<dependencies>
<module name="test.test"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
<dependencies>
は使いたい静的モジュールの名前です。<exclusions>
は使いたくない暗黙的依存関係のモジュールの名前になります。ここで再度、helloworld.war.dodeploy
ファイルを作成してデプロイしなおし http://localhost:8080/helloworld/logger.jsp
にアクセスします。
以下のように表示されました。 module という内容の String 型が戻ってきていますので、インストールした test.test モジュールのクラスが使用されたことが分かります。
java.lang.String
module
想定通りに動きました
###動的モジュールが使用される?
次に動的モジュールのみ使用するように設定します。 jboss-deployment-structure.xml
を以下の内容に変更します。暗黙的依存関係モジュールを除外する設定のみ残します。
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
<deployment>
<exclusions>
<module name="org.apache.log4j" />
</exclusions>
</deployment>
</jboss-deployment-structure>
再度、helloworld.war.dodeploy
ファイルを作成してデプロイしなおし http://localhost:8080/helloworld/logger.jsp
にアクセスします。
以下のように表示されました。 war という内容の String 型が戻ってきていますので、WEB-INF/lib にある動的モジュール classloadTestInWar.jar のクラスが使用されたことが分かります。
java.lang.String
war
想定通りに動きました
#まとめ
WildFlyがクラスをロードする優先順位を意識していないと、作成したアプリケーションの中に暗黙的依存関係のモジュールと同じ物が含まれていた場合、バージョンの違などによる思わぬエラーに悩まされることがありますのでご注意下さい。
#参考資料
-
暗黙的依存関係のモジュール一覧 ⇒ 2.3. Which are the implicit module dependencies?
第6章 JBoss EAP クラスローディング ↩ ↩2 -
クラスローディングの優先順 ⇒ 1.3. Class Loading Precedence ↩