[Seasar-user:5893] Re: [Teeda]ThrowsInterceptorでthrowした例外が初回はcatchできない(hotdeploy)

Koichi Kobayashi [E-MAIL ADDRESS DELETED]
2007年 1月 25日 (木) 18:00:44 JST


小林 (koichik) です.

Date:    Thu, 25 Jan 2007 16:17:54 +0900
From:    Asarima <[E-MAIL ADDRESS DELETED]>
To:       [E-MAIL ADDRESS DELETED]
Subject: [Seasar-user:5889] [Teeda]ThrowsInterceptorでthrowした例外が初回はcatchできない(hotdeploy)

>  このあたりの仕組みが全然理解できずにでおり、困っております。
>  申し訳ありませんが、なぜこうなるのか分かれば教えてください。

HOT deploy は HotClassLoader というクラスローダーを
リクエストごとに作り,そのたびに新たにクラスを
ロードし直すという方法で実現しています.

ただし,全てのクラスが HotClassLader でロードできる
わけではありません.
現在の HOT deploy では,dicon ファイルに定義した
コンポーネントのクラスは HotClassLoader が動き出す前に
ロードされてしまうため,HOT の対象ではありません.

それで,ですね.
HOT の対象でない,WebAppClassLoader などでロードされた
クラスからは,HotClassLoader が見えないため,HOT 対象外の
クラスが参照するクラスもまた,HOT にできないということに
なります.

例えば,FooPage が FooService を呼び出し,その結果
FooDto が返されるとします.
これら全てが HOT deploy 対象であれば問題ありません.
しかし,FooPage が dicon に定義されていると,FooPage は
HOT ではなくなり,通常のクラスローダーからロードされます.
そして FooService を呼び出します.これは HOT 対象なので,
HotClassLoader からロードされます.
FooService は FooDto を返します.これも HotClassLoader から
ロードされます.
問題はその次です.
FooPage は通常のクラスローダーからロードされているため,
FooPage が参照する FooDto も通常のクラスローダーから
ロード(リンク)されます.
Java では同じ名前のクラスでも,同じ .class ファイルから
ロードされたクラスでも,異なったクラスローダーから
ロードしたクラスは別のクラスとして扱われます.
その結果,FooPage が FooService からの戻り値を
FooDto 型の変数に設定するところで ClassCastException が
発生することになります.
お分かり頂けるでしょうか?

このような問題を極力回避するため,HotClassLoader は
通常のクラスローダーでロード済みのクラスは HOT の
対象から除外するようにしています.
上の例では,FooPage と共に FooDto がロードされれば,
HotClassLoader は FooDto をロードしないため,FooService も
通常のクラスローダーがロードした FooDto を使用します.
そのため,ClassCastException は発生しません.

しかし,特に Java5 では,メソッドの中で使っている
クラスのロードは実行に必要になるまでロードされない
傾向にあります.
そのため,例え FooPage が FooService を呼び出すコードが

FooDto dto = fooService.hoge();

のようになっていたとしても,FooService が呼び出される
時点では FooDto はロードされておらず,結果として
FooDto は HotClassLoader にロードされる可能性があります.
そして ClassCastException に...

ここで,もう一度 FooService が呼ばれると,今度は
FooDto が本来のクラスローダーにロードされているため,
FooService は HOT ではない FooDto を返します.
HOT で 2 回目以降はうまく動くというのは,このような
状況で発生します.
# うまく動いてはいるけど,HOT ではない.


Asarima さんのケースもこれと同じ状況です.
ThrowsInterceptor は,dicon ファイルに定義して
使います.
そのため,ThrowsInterceptor は HOT の対象ではありません.
そして,ThrowsInterceptor のメソッドで受け取る例外の
クラスもまた,HOT の対象ではありません.

そこで,次のような動きになります.
最初の実行時には,例外クラスは本来のクラスローダーに
ロードされていません.
そのため,例外は HotClassLoader によってロードされます.
その例外を ThrowsInterceptor がキャッチします.
ここで,ハンドラメソッドで受け取る例外クラスが
本来のクラスローダーによってロードされます.
しかし,キャッチした例外とはクラスローダーが
異なるため,ハンドラが存在しないものとして
そのまま再スローされます.

2 回目の場合は,すでに例外クラスは本来のクラスローダーに
ロードされているため,HotClassLoader ではロード
されません.
そのため,ThrowsInterceptor はその例外をキャッチ
することができます.


さて,対応策ですが...
根本的なところでは,dicon に定義したコンポーネントも
HOT deploy の対象にしたいと考えています.
しかし,これには時間がかかりそうです.

そこで,ThrowsInterceptor の初期化時に,ハンドラメソッドの
引数に指定されているクラスをロードするようにしたいと思います.
それにより,初回実行時から例外クラスは本来のクラスローダーに
ロードされるようになります.
# HOT の対象にはならなくなりますが.


--
<signature>
   <name>Koichi Kobayashi</name>
   <e-mail>[E-MAIL ADDRESS DELETED]</e-mail>
</signature>




Seasar-user メーリングリストの案内