LoginSignup
11
9

More than 5 years have passed since last update.

JBossのモジュールとアプリケーションのライブラリの競合を防ぐ方法

Posted at

確認環境

JBoss EAP 6.4.0, 7.0.0, 7.1.0beta1

モジュールとライブラリの競合が起こる原因

Java EEアプリケーションサーバとしてJBoss EAP(以下JBoss)を使う際の注意点としてライブラリとモジュールの競合があります。
詳細については、開発ガイドの3章クラスローディングとモジュールに記載されていますが、簡潔に説明すると、JBossではアプリケーションのライブラリよりもJBossに同梱されているモジュールが優先してロードされます。(ざっと見た感じ順番を変える設定はなさそうです。)

つまり、アプリケーションがビルド時に使用したライブラリのバージョンと異なるバージョンで実行される可能性があり、起動時や実行時にエラーが発生するかもしれません。(今は大丈夫でもバージョンアップしたときに問題が起こるかも:anguished:

ロードされるモジュールを把握する

JBossのモジュールはデプロイしたものによって自動的にロードされるので、動かしたいアプリケーションを1回デプロイしてみて、何がロードされるか把握しましょう。

{JBOSS_HOME}/standalone/configurationにあるstandalone.xmlのデフォだと115行目くらいにログ出力に関する設定があるので、levelをDEBUGに変更します。

standalone.xml
  <root-logger>
   <level name="DEBUG"/>
   <handlers>
    <handler name="CONSOLE"/>
    <handler name="FILE"/>
   </handlers>
  </root-logger>

この状態でJBossを起動して何かアプリケーションをデプロイしてみてください。
すると{JBOSS_HOME}/standalone/server.logにアプリケーションのライブラリとJBossがロードしたモジュールの一覧が出力されています。
Adding resourceと書かれている部分がwarから読み込まれたライブラリで、
Adding dependency ModuleDependencyと書かれている部分がロードされたモジュールです。
以下は出力例です。

server.log
2017-09-04 18:29:42,597 DEBUG [org.jboss.as.server.deployment] (MSC service thread 1-7) Adding resource "/C:/jboss/standalone/deployments/todo.war/WEB-INF/lib/aopalliance-1.0.jar" to module deployment.todo.war
2017-09-04 18:29:42,648 DEBUG [org.jboss.as.server.deployment] (MSC service thread 1-7) Adding dependency ModuleDependency [identifier=javax.ejb.api, moduleLoader=local module loader @282ba1e (finder: local module finder @13b6d03 (roots: C:\jboss\modules,C:\jboss\modules\system\layers\base)), export=false, optional=false, importServices=true] to module deployment.todo.war

今回は例としてjavax.ejb.apiを除外してみます。

モジュールに関する設定ファイルの作成

WebアプリケーションのWEB-INF直下にjboss-deployment-structure.xmlを配置します。
配置したら以下のように設定します。

jboss-deployment-structure.xml
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <deployment>
        <exclusions>
            <module name="javax.ejb.api" />
        </exclusions>
    </deployment>
</jboss-deployment-structure>

除外するモジュールを<exclusions>で囲うだけです。
パスから指定する必要がありますが、これは各モジュールのmodule.xmlに書かれています。
では、この状態でJBossを再起動してみます。

ログが長いので割愛しますが、Adding dependency ModuleDependencyの欄にjavax.ejb.apiがないはずです。

その他の設定

jboss-deployment-structure.xmlには、もともとJBossに含まれないモジュールを追加したり、複数のモジュールで構成されるサブシステムごと除外したりできます。ここでは取り上げませんが、設定方法が開発ガイドの3章に書かれていますので参考にしてください。

この投稿に至った経緯

Tomcatで問題なく動くアプリケーションがJBossだとエラーになる原因がわからず四苦八苦したので:dizzy_face:

おまけ

具体的な事例

こっちをメインに書くか悩んだのですが、単にモジュールを除外設定すればいいという話ではなかったので、おまけとして……
TERASOLUNA(5.3.0)のブランクプロジェクトを作成し、HelloController.javaに以下のコードを追加します。

HelloController.java

/** (略 **/
import com.fasterxml.jackson.databind.util.StdDateFormat;

    /** (略 **/

    @RequestMapping(value = "/", method = { RequestMethod.GET, RequestMethod.POST })
    public String home(Locale locale, Model model) {
        logger.info("Welcome home! The client locale is {}.", locale);

        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
        String formattedDate = dateFormat.format(date);

        // ここから
        Set<DateFormat> set = new HashSet<>();
        set.add(StdDateFormat.instance);
        // ここまで

        model.addAttribute("serverTime", formattedDate);

        return "welcome/home";
    }
}

これをSTS同梱のPivotal tc Server(v3.2)にデプロイしてhttp://localhost:8080/todoにアクセスしてみます。

pivotal_result.PNG

特に問題はありませんね。
では次にJBoss EAP 7.0.0にデプロイしてアクセスしてみましょう。

jboss7_result.PNG

なんかエラーになりました。コンソールにもエラーが出ています。

console.log
15:28:42,923 ERROR [org.terasoluna.gfw.common.exception.ExceptionLogger] (default task-2) [e.xx.fw.9001] UNDEFINED-MESSAGE: java.lang.NullPointerException
    at java.text.DateFormat.hashCode(Unknown Source)
    at java.util.HashMap.hash(Unknown Source)
    at java.util.HashMap.put(Unknown Source)
    at java.util.HashSet.add(Unknown Source)
    at todo.app.welcome.HelloController.home(HelloController.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    (略)

HelloController.javaの38行目はset.add(StdDateFormat.instance);の部分です。

このエラーは報告されていて、jackson-databindの2.7.2で既に修正されています。
TERASOLUNA 5.3.0が利用するjackson-databindのバージョンはスタック一覧にあるように、2.8.5なのですが、なぜこのような問題が起こったのでしょうか?

それは、JBoss EAP 7.0.0に含まれるjackson-databindのバージョンが2.5.4だからです……が、複数のモジュール同士が依存関係にあり、単にjackson-databindを除外してもうまく動かないので、サブシステムJAX-RSを除外します。

参考:Wildfly 9 - How do I exclude Jackson

jboss-deployment-structure.xmlを以下のように設定します。

jboss-deployment-structure.xml
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <deployment>
        <exclude-subsystems>
            <subsystem name="jaxrs" />
        </exclude-subsystems>
    </deployment>
</jboss-deployment-structure>

では、この状態でJBossを再起動してみます。

jboss7_result.PNG

やったぜ☆

11
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9