Jetty:WebAppContext.setParentLoaderPriority

この問題の背景

あるプログラム(これを本体とする)の中でJettyを組み込みとして使い、ウェブアプリをホスティングする。つまり、通常のJavaプログラムとしても機能しつつ、ウェブサーバとしてウェブアプリをホスティングする。

しかし、ウェブアプリ内のコード自体にあらかじめアクセスしたいため(この理由は後述)、本体プログラムのライブラリとしてウェブアプリのWEB-INF/classesフォルダを指定しておく。これにより、WEB-INF/classes中のクラス(Aとする)に本体側からアクセスが可能になる。

さらに、ウェブアプリ側にはWEB-INF/libは入れない。なぜなら、クラスAが使用するライブラリは本体側になければならないからだ。必要なものはすべて本体側のライブラリとして指定しておく。

問題の発生

この状態でウェブアプリを走行させてみると、ウェブアプリで必要なライブラリは本体側にあり、既に本体側ローダで一部ロードされているが、当然ながらウェブアプリのclasses自体は本体側にはロードされていない。それらは、ウェブアプリ専用のローダでロードされる。

ここで、ウェブアプリがその中のクラスBを使用する場合で、その上位クラスCがライブラリにあり、Cが既に本体側でロードされていると困ったことになる。

本体側ローダのクラスCは、ウェブアプリローダのクラスBとは何の関係も無くなってしまう。字面上では、BはCのサブクラスだが、クラスローダが異なるため、無関係になってしまうのだ。

解決策として考えられること

二つの解決策が考えられた。

  • ウェブアプリのclassesにあるクラスすべてをあらかじめ本体ローダでロードしておく。
  • 何らかの方法で、ウェブアプリに独自ローダを使わせないようにJettyに細工する。

実際の解決策

Jettyのクラスローダに関する説明が、Jetty Classloadingにある。

これによれば、サーブレットの仕様としては、ウェブアプリ側のローダによるクラスロードが優先されるのだが、しかし、Jettyのオプションによって本体側ローダを優先することができるという。

このケースの場合、本体側のクラスパスにあるものとウェブアプリのclassesは全く同じものを指しているので、単純に本体側のローダを優先させればよい。

つまり、単純に、

    WebAppContext warContext = new WebAppContext();
    warContext.setParentLoaderPriority(true);

とすれば良いことがわかる。