クラスローダ/ デリゲーション・モード

デリゲーション・モード
WARクラスローダそのものを単純標準出力した結果
--------------------
com.ibm.ws.classloader.CompoundClassLoader@1f7010c3
 Local ClassPath: ...(略)...installedAppslemonNode02Cell
               luv-app.earluv.warWEB-INFclasses;
          ...(略)...installedAppslemonNode02Cell
               luv-app.earluv.war;
 Delegation Mode: PARENT_FIRST
--------------------
該当クラスローダに関する情報が出力されるように、
クラスローダのtoString()がオーバーライドされている。
これで、該当クラスローダのローカルクラスパス(LocalClassPath)が分かる。

[Delegation Mode: PARENT_FIRST]ゆう出力結果は、
クラスローダデリゲーション・モード(Delegation Mode)が、PARENT_FIRSTであることを示す。


デリゲーション・モードの設定によって、デリゲーションモデルが決まる。
選択できるモードは2つ。
・PARENT_FIRST
・PARENT_LAST

Java基本クラスローダ/ブーストトラップクラスローダ
 (loads /lib/*.jar)

|―Java基本クラスローダ/エクステンションクラスローダ
   (loads /lib/ext/*.jar)

|―Java基本クラスローダ/システムクラスローダ
   (loads CLASSPATH)
  |
  |―WebSphere/EXTクラスローダ
     (loads /lib/*.jar)
    |
    |―banking.ear/アプリケーションクラスローダ
       (loads banking-ejb.jar, utility.jar)
    | |
    | |―customer.war/WARクラスローダ
    | |
    | |―admin.war/WARクラスローダ
    |
    |―trading.ear/アプリケーションクラスローダ
       (loads trading-ejb.jar, utility.jar)
      |
      |―company.war/WARクラスローダ


上記のうち、アプリケーション・クラスローダとWARクラスローダについては、
デリゲーション・モードをカスタマイズできる。

■PARENT_FIRST(デフォルト)
 自クラスローダのローカルクラスパスを探す前に、最初に親クラスローダにデリゲートする。
 親で見つからなかった時に初めて、自分のローカル・クラスパスを探しにく。

■PARENT_LAST
 自クラスローダのローカル・クラスパスを最初に探す。
 見つからなかった時に初めて、親クラスローダにデリゲートする。


■探索順序
デリゲーション・モードの設定次第で、クラスを探す順序が変わる。

ローカル・クラスパスデリゲーション・モード設定
(A)
APP-FIRST
WAR-FIRST
(B)
APP-LAST
WAR-FIRST
(C)
APP-FIRST
WAR-LAST
(D)
APP-LAST
WAR-LAST
Java基本クラスローダー1223
WebSphere Ext
クラスローダー
2334
アプリケーション
クラスローダー
3142
WARクラスローダー4411


WebSphereApplicationServerの管理コンソール上では、
デリゲーション・モードをクラス・ローダ・モード(Class loader mode)と表記している。


■ケーススタディ
PARENT_LASTはいつ役立つのか?

某月某日
A:jakartaプロジェクトのライブラリのhoge-1.2.jar使うてんねんけど
  どうも挙動がおかしいねん。
B:ちゃんとWEB-INF/libに置いとうと?
A:置いとうよ。クラスを見には行ってるようなんやけど…。
  観てみたら、WebSphereのインストールしたディレクト(WAS_HOME)下の
  libディレクトリ内にhoge-1.1.jarがあったわ。
  自分らのAP内に置いたWEB-INF/lib/hoge-1.2.jarやのうて、
  WAS_HOMEのを先に使うてるようやわ。
B:あかんやん!WebSphereでhoge-1.2.jarは使えんゆうこと?
A:WAS_HOME/lib/hoge-1.1.jarをhoge-1.2.jarで置き換えちゃばええちゃうん?
B:いいね。やってみて~
A:あかん。WebSphere自体が立ち上がらんくなってもうた…。

解説
Aがやるべきやったんは、jarの置き換えやのうて、デリゲーション・モードをPARENT_LASTにすること!
デリゲーション・モードを変更することで、
WebSphereそのものに影響を全く与えずに、
自分のWebアプリケーション内に含まれるライブラリが先に優先されるようになる。

→クラスローダが複数ある利点。


■PARENT_LASTの必要性
WebSphere Application Serverの内部ライブラリに影響されることなく
アプリケーションに最新のライブラリを用いたい場合に[PARENT_LAST]が有効

サーブレットの仕様でも
It is recommended also that the application class loader be implemented so that classes and resources packaged within the WAR are loaded in preference to classes and resources residing in container-wide library JARs.
(アプリケーションのクラスをロードするクラスローダーは、アプリケーション・サーバー、Webコンテナ全体で使用するライブラリー内よりも、WAR内部のクラスやリソースを優先してロードすることが推奨される。)
ref: 2.4 Specification SRV.9.7.2

なぜだか
websphereは、PARENT_FIRSTがデフォルトだけど..

注:Java基本クラスローダーが担当するコアクラス(java.lang.*など)に関しては、
  Javaのセキュリティー・モデルで禁止されているため、
  たとえ「PARENT LAST」にしても、「上書き」はできない。


□ちなみに WebコンテナTomcat
Tomcatのクラスローダ・ツリー
 Bootstrap
 |
 |―System
   |
   |―Common
     |
     |―Catalina
     |
     |―Shared
       |
       |―Webapp1
       |
       |―Webapp2

ref: The Apache Jakarta Tomcat 5.5 Servlet/JSP Container Class Loader HOW-TO

Tomcatの場合、EARがなく、Webアプリケーション(WAR)だけなので若干違いがあるが、
tomcat#common、share配下よりも各webapps内のクラス・リソースが優先される
とドキュメントに明記されている。
つまり、PARENT_LASTの設定。

■シングルトンがシングルトンでなくなる日
PARENT_LASTとすべきだが、
通常のJavaのクラスローダのデリゲーション・モードは、PARENT_FIRST。

―typical singleton pattern class―
package com.example;
public class Singleton {
  
  private static Singleton instance = new Singleton();
  private int counter;
  private Singleton() {}
  
  public static Singleton getInstance() {
    return instance;
  }
  
  public void increment(int i) {
    counter += i;
  }
  
  public int getCounter() {
    return counter;
  }

}
―/typical singleton pattern class―

1つしかインスタンス生成されない、
というシングルトンクラスのルールでも
以下ルールで破られる。

―クラスの同一性に関するルール―
全く同じパッケージ名・同じクラスでも、
異なるクラスローダからロードされたものは、
同一クラスとして扱われずに、別クラス扱いとなる。
―/クラスの同一性に関するルール―


□実験

―EAR構成―
luv-app.ear
 |― luv-ejb.jar
 |   |― com.example.Singleton
 |   |― com.example.ejb.InEJB
 |― luv.war
    |― WEB-INF/
       |― classes/
       |― com.example.Singleton
       |― com.example.web.ClassLoaderTestServlet
―/EAR構成―

Singletonクラスを、EJBモジュールとWebモジュールの双方に配置。
アプリケーション・クラスローダとWARクラスローダ
両方のデリゲーション・モードをPARENT_LASTに設定。

―test classes―
package com.example.web;

import com.example.Singleton;
import com.example.ejb.InEJB;
...
public class ClassLoaderTestServlet extends HttpServlet {

  protected void service(HttpServletRequest req,
  HttpServletResponse res)
      throws ServletException, IOException {
    testSingleton();
  }

  private void testSingleton() {
    Singleton singleton = Singleton.getInstance();

    System.out.println("1. InWeb - counter: "
             + singleton.getCounter());
    System.out.println("counter: +1");
    singleton.increment(1);
    System.out.println("2. InWeb - counter: "
             + singleton.getCounter());    
    InEJB.testSingleton();
    System.out.println("5. InWeb - counter: "
             + singleton.getCounter());
    
  }
}

package com.example.ejb;

import com.example.Singleton
...

public class InEJB {

  public static void testSingleton() {
    Singleton singleton = Singleton.getInstance();

    System.out.println("3. InEJB - counter: "
             + singleton.getCounter());
    System.out.println("counter: +10");
    singleton.increment(10);
    System.out.println("4. InEJB - counter: "
             + singleton.getCounter());
  }
  
  public static Singleton getSingleton() {
    return Singleton.getInstance();
  }
}
―/test classes―

Webモジュール内のサーブレットClassLoaderTestServletと、
EJBモジュール内のクラスInEJBの両方から
同一のシングルトン・クラスcom.sample.Singletonを使用。


Singletonクラスが1インスタンスしか生成されていない場合、
実行の結果、最後に出力される[counter]は[11]のはず。

―1インスタンスしか生成されていない場合の実行結果―
1. InWeb - counter: 0
counter: +1
2. InWeb - counter: 1
3. InEJB - counter: 1
counter: +10
4. InEJB - counter: 11
5. InWeb - counter: 11
―1インスタンスしか生成されていない場合の実行結果―

が、実際はPARENT_LASTモードでシングルトンでなくなってる。

―実際の実行結果―
1. InWeb - counter: 0
counter: +1
2. InWeb - counter: 1
3. InEJB - counter: 0
counter: +10
4. InEJB - counter: 10
5. InWeb - counter: 1
―/実際の実行結果―

Webモジュール内のサーブレット、EJBモジュール内のInEJBは
それぞれ異なるシングルトンのインスタンスを参照している。

PARENT_LASTモードとなっているため、
サーブレット(@子クラスローダ)は、
WARクラスローダ(子)内にロードされたシングルトンクラスを先に見つける。
一方、
InEJBクラスは、EJBモジュール(親)内にロードされたシングルトンクラスを最初に見つける。

結果、別々のSingletonクラスを参照。
当然、static変数インスタンスもそれぞれのクラスに存在。
→シングルトンのインスタンスが2つ!

PARENT_FIRSTに設定した場合、
シングルトンインスタンスは1つしか生成されず、
JVM全体でシングルトンの役割を果たす。


1つのJVM内で、クラスを識別するキーとなるのは
[パッケージ名+クラス名]ではなく、
[パッケージ名+クラス名+ロードしたクラスローダ]が識別キーとなる。


http://www-06.ibm.com/jp/software/websphere/developer/j2ee/strategy/2.html

tag : クラスローダ デリゲーション・モード WebSphere

2008-09-01 22:53 : j2ee : コメント : 0 : トラックバック : 0 :
コメントの投稿
非公開コメント

« next  ホーム  prev »

search

ad



counter


tag cloud

category cloud