Wicketのホットデプロイの範囲指定の罠(後編)

はじめに

 「Wicketホットデプロイの範囲指定の罠(前編)」の続きです。
 自分の中では旬は過ぎてしまったのですが、前編だけでは気持ち悪いので…。

リローディング後に、古いクラスオブジェクトと関連づいたオブジェクトが残る件

どうして発生するのか

 リローディング発生後も生き残っているオブジェクトが存在する場合、下図の状態が発生することがあります。厳密さには欠ける図ですけれども。

 下図の以下のクラスがリローディング対象です

  • HomePage
  • ABean

 下図の以下のクラスがリローディング対象外です

  • ExcludeClass



 つまり、リローディング対象のクラスについて、リローディング前と後のクラスが混在します。
 なぜならば、一度生成されてしまったオブジェクトに結びついているクラスは破棄されないからです。
 これは型変換に失敗する原因になります。

実験方法

 型変換に失敗することを確認する実験します。
 まず、適当にBeanを用意します。
 前編で使用したものと同じでよいので略。

 Beanを利用するReloading対象外クラスを用意します。  で、クラス図を載せた際に「厳密さには欠ける図」と記述したのはここに掛かってます。Beanの具象クラス名はソース中に書きません。
 ここでは全メンバをstaticにします。

package wicketReloading.excludePackage;


public class ExcludeClass {

    private static Object obj = null;

    public static Object getObj() {
        return obj;
    }

    public static void setObj(Object obj) {
        ExcludeClass.obj = obj;
    }

}


 最後に、ページクラスを用意します。
 たまにある「最初に使うときに1回だけ初期化」という実装をします。
 ABeanのインスタンスを使い回そうという魂胆です。

package wicketReloading.page;

import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;

import wicketReloading.Bean.ABean;
import wicketReloading.excludePackage.ExcludeClass;

public class HomePage extends WebPage {

    private static final long serialVersionUID = 1L;

    public HomePage(final PageParameters parameters) {
        if(ExcludeClass.getObj() == null){
            ExcludeClass.setObj(new ABean());
        }
        Object obj = ExcludeClass.getObj();
        ABean aBean = (ABean)obj;
        add(new Label("message", aBean.getMessage()));
    }


リローディング発生後に画面を表示しようとすると下図のようなエラーが表示されます。


さいごに

 「こんなのはまらないよ」と思われるかもしれませんが、意外と私ははまりました。
 Factory Methodパターンを使ったときとか、オブジェクトをキャッシュしているときとか。
 ホットデプロイを使う際は心の片隅にこのことをとどめておこうと思います。