Wicketがクラスローダを差し変える方法

はじめに

 WicketがReloding対応のためにクラスローダを差し替える方法について、基本的なところをメモします。
 「基本的なところ」と書いたのは細部に謎が残っているからです。
 私の好奇心を満たせる程度には分かったので、まあいいや、みたいな。

環境


実装方法の基本的なところ

 currentThreadのcontextClassLoaderにReloadingClassLoaderのインスタンスを突っ込んでおいて、適切な場面でそれを取り出すことになります。
 currentThreadはJava標準のアレです。

currentThreadのcontextClassLoaderに突っ込むタイミング

 以下の2カ所です。

  • フィルタ初期化時(WicketFilter.init()メソッド)
  • リクエストがきたとき(WicketFilter.doGet()メソッド)


 下記はWicketFilter.init()の抜粋です。
 引用部分でgetClassLoader()を使用しています。ReloadingWicketFilter使用時にはgetClassLoader()はReloadingClassLoaderのインスタンスを返します。
 結果ReloadingClassLoaderのインスタンスが、Thread.currentThread().setContextClassLoader()メソッドの引数になります。

final ClassLoader previousClassLoader = Thread.currentThread().getContextClassLoader();
final ClassLoader newClassLoader = getClassLoader();
try
{
    if (previousClassLoader != newClassLoader)
    {
        Thread.currentThread().setContextClassLoader(newClassLoader);
    }
(以下略)


 ReloadingWicketFilter使用時のクラス図は以下のようになっています。
 getClassLoader()がオーバーロードされるわけです。

 WicketFilter.doGet()メソッドも類似の処理が記述されています。

currentThreadのcontextClassLoaderから取り出すところ

 ここが「私の好奇心を満たせる程度には分かったので〜」と書いてしまった理由だったりします。
 currentThreadのcontextClassLoaderを取り出す際には、getContextClassLoader()を使用するわけですが、「必然性があってgetContextClassLoader()で取り出しているか?」を全部チェックするのはちょっとなぁ、みたいなところがありまして。
 getContextClassLoader()、クラスローダが欲しいときには結構普通に使いますしね。

 現在分かっている範囲でgetContextClassLoader()が必要だと私が言える箇所は、Applicationクラス(実際にはサブクラスですけども)のインスタンス生成です。

 ここで、「自分を生成したクラスローダにクラス名の解決を依頼する」というJavaの仕様が生きてきます。
 Wicketを利用してアプリを書く際にApplicationクラスが基点になりいろいろなオブジェクトを生成するわけです。よって、Applicationを生成するクラスローダをReloadingClassLoaderにしておけば、Appicationクラスが生成するオブジェクトもReloadingClassLoaderによってロードされたクラスに基づいて生成されます。これでReloadingClassLoaderの使用が伝播していきます。

 WicketFilter.init()では、ファクトリ内でgetContextClassLoader()を使用してReloadingClassLoaderを取得しています。
 デフォルトではContextParamWebApplicationFactory.createApplication()メソッドがAppicationオブジェクトを生成します。下記に一部抜粋します。

protected WebApplication createApplication(final String applicationClassName)
{
    try
    {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader == null)
        {
            loader = getClass().getClassLoader();
        }
        final Class applicationClass = loader.loadClass(applicationClassName);
        if (WebApplication.class.isAssignableFrom(applicationClass))
        {
            // Construct WebApplication subclass
            return (WebApplication)applicationClass.newInstance();
        }
(以下略)


最後に

 細かい謎は残っているのですが、クラスローダの差し替えについての調査は、ここまでにしようかと今は思っています。