JBoss ASのクラスローディング
前回のクラスローダの話に引き続き、JBoss ASのクラスローディングについて書いておく。
JBoss ASのクラスローダの実装面の細かい話とか、ブートストラップクラスローダとシステムクラスローダの違いだとかendorsedとかのお役立ち度の低い細かい話は対象外。利用者が最低限抑えておきたいところ、という観点で書く。
基本的な構造
JBoss ASのクラスローディングは基本的には以下の構造になっている。
- システムクラスローダ
- JBoss AS共通部
- EAR
- WAR
- EAR
- JBoss AS共通部
ロード順はもちろん子優先(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からはデフォルトで分離されている。
よくある問題
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