nekop's blog

OpenShift / JBoss / WildFly / Infinispanの中の人 http://twitter.com/nekop

JBoss ASのクラスローディング

前回のクラスローダの話に引き続き、JBoss ASのクラスローディングについて書いておく。

JBoss ASのクラスローダの実装面の細かい話とか、ブートストラップクラスローダとシステムクラスローダの違いだとかendorsedとかのお役立ち度の低い細かい話は対象外。利用者が最低限抑えておきたいところ、という観点で書く。

基本的な構造

JBoss ASのクラスローディングは基本的には以下の構造になっている。

  • システムクラスローダ
    • JBoss AS共通部
      • EAR
        • WAR

ロード順はもちろん子優先(Child-firstもしくはParent-lastと呼ばれる)。たったの4階層だし、特に難しいことや困ることは無いはず。

EARの分離モード

JBoss ASでは一点だけ注意するところがある。JBoss ASは歴史的な理由から、EARのクラスローディング分離がデフォルトでOFFになっている。これは、上の構造で挙げた「JBoss AS共通部」と「EAR」が分離されていないことを示す。このままでは「JBoss AS共通部」と「EAR」両方で、もしくは2つの別のアプリケーション「EAR1」と「EAR2」両方で、同じクラスを持つjarファイルがあったりした場合に、期待したクラスがロードされないという問題が発生してしまうので、分離したほうがよい。JBoss AS 6からはデフォルトで分離されている。

  • JBoss AS 4.2.x
    • deploy/ear-deployer.xmlのIsolatedをtrueにする
  • JBoss AS 5.x
    • deployers/ear-deployer-jboss-beans.xmlのisolatedをtrueにする
  • JBoss AS 6.x
    • JBoss AS 5.xと同じだけどデフォルトでtrueになっているので問題なし

上の方法の他に、EARのMETA-INF/jboss-app.xmlで指定する方法もあるけど面倒なだけなので省略。

よくある問題

ClassNotFoundException。パッケージングミスや、パッケージ間の依存関係の設計のミス。具体例としては、EAR内のEJBから、WAR内のクラスを参照したりしてしまったようなとき。上の構造図を見ても分かる通り、EARからWARというのは依存関係が逆方向になるので参照できないよ。

ClassCastExceptionその1。EARとWARの両方に同じjarやクラスが重複してパッケージングされているというパッケージングミス。クラスがWAR内のみで利用されるならWARへ、そうじゃないならEARへ。両方はダメ。

ClassCastExceptionその2。JBoss AS側でも持っているjavaxパッケージのクラスなどがEARやWARにパッケージングされていて、JBoss AS側がロードしたものと衝突するパターン。Java EEに含まれるクラスはアプリケーション側ではなくコンテナ側でロードされるべきなので、EARやWARに含めてはならない。jarの具体例を挙げるとservlet-api.jarとかjta.jarとかj2ee.jarとかgeronimo-なんとか.jarとか。TomcatなどのJava EEコンテナじゃないものからの移行などでよく発生するパッケージングミス。

番外編。xml-apis.jarが含まれている場合にコケる。xml-apis.jarに含まれているパッケージっていうのはとっくの前(1.4)にJava本体に含まれており、システムクラスローダで読み込まれているので、これをEARとかWARとかに配置するのはご法度。java.lang.Stringクラスを自前で定義して含めちゃう、というレベルの問題とまったく一緒。やっちゃダメ絶対。

java.lang.ClassCastException: org.apache.xerces.jaxp.SAXParserFactoryImpl cannot be cast to javax.xml.parsers.SAXParserFactory