IBM®
本文へジャンプ
    Japan [変更]    ご利用条件
 
 
検索範囲検索:    
    ホーム    製品    サービス & ソリューション    サポート & ダウンロード    マイアカウント    
skip to main content

developerWorks Japan  >  WebSphere  >

クラスローダーとJ2EEパッケージング戦略を理解する: 第4回「ディフェンシブ・パッケージング」

developerWorks
ページオプション

JavaScript を要するドキュメントオプションは表示されません


レベル: 上級

夷藤 勇人, ソフトウェア事業, IBM

2005年 12月 14日

J2EE開発プロジェクトにおいて必要不可欠なJ2EEパッケージング戦略とはなにか?すべてのJ2EE開発者が正しいJ2EEパッケージング戦略をとれるように、詳細にその理論・方法を解説していきます。

はじめに

全5回シリーズ「クラスローダーとJ2EEパッケージング戦略を理解する」、第3回となる前回は、クラスローダーがもたらすドッペルゲンガー現象について学びました。

第4回となる今回は、ディフェンシブ・パッケージングについて解説します。ディフェンシブ・パッケージングとは、上位クラスローダー配下に置いてある「クラスやリソース」の影響を受けないように、自アプリケーションに防衛策を施しておくパッケージング戦略のことです。アプリケーションのディフェンスが弱いと、容易に「侵入」を許してしまい、予期せぬ動作をしてしまうことがあります。




上に戻る


ケーススタディcommons-logging

今回は、ロギング・フレームワークとして有名なJakartaプロジェクトのcommons-loggingを例として取り上げます。commons-loggingを使用したWebアプリケーションのパッケージング例です。

commons-logging (US)
リスト1 commons-loggingを使用したWebアプリケーション
                
luv-war
 - WEB-INF/classes
     - com/example/MyHello
     - ...
 - WEB-INF/lib
     - commons-logging.jar


commons-loggingの使用例です。


リスト2 commons-loggingの使用例:MyHelloクラス
                
...
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class MyHello {
    static final Log log = LogFactory.getLog(MyHello.class);
    void hello() {
        log.info("Hello");
    }
    ...
}


commons-loggingは他のロギング・ライブラリーへのラッパーとして動作します。commons-loggingと組み合わせるものとしては、

  • Apacheプロジェクトのロギング・ライブラリー・Log4j
  • J2SE1.4から導入された標準のjava.util.loggingパッケージ

などが一般的です。今回の例では明示的に何を使用するかを指定していません。この場合は、通常、java.util.loggingパッケージが自動的に使用されます。

アプリケーションでは、commons-loggingで提供されているクラス

  • org.apache.commons.logging.Log;
  • org.apache.commons.logging.LogFactory;

のみを使用しますが、実際にはjava.util.loggingパッケージを利用する実装が選択され、ロギングそのものはjava.util.loggingパッケージングが最終的に行います。




上に戻る


ドッペルゲンガー再び

一見、何の問題もないようですが、WebSphere Application Server V6(またはV5)上でリスト1のWebアプリケーションを動作させると、エラーが発生してしまいます(リスト3)。


リスト3実行結果エラーメッセージ
                
...
Caused by: org.apache.commons.logging.LogConfigurationException:
The chosen LogFactory implementation does not extend LogFactory.
Please check your configuration.
(Caused by java.lang.ClassCastException: com.ibm.ws.commons.logging.TrLogFactory)
...


エラーメッセージからは

1.LogFactoryの実装として「com.ibm.ws.commons.logging.TrLogLogFactory」が選択された
2.選択されたTrLogFactoryはLogFactoryのサブクラスではない

このため「ClassCastException」がおきてしまったことが読み取れます。
「TrLogFactory」とは、WebSphereが独自に提供しているcommons-loggingのログファクトリーの実装クラスです。なぜ意図に反してこの実装が選択されたのでしょうか? 原因は、commons-logging.propertiesファイルにあります。このプロパティーファイルは、commons-loggingがロギング実装を選択する際に使用されます。

commons-logging.propertiesがどこにあるのか確かめてみましょう。:


                
getClass().getClassLoader.getResource("commons-logging.properties")
  

結果


                
jar:file:/.....<WAS_HOME>/lib/ws-commons-logging.jar!
/commons-logging.properties


commons-logging.propertiesファイルは、WebSphereのインストールディレクトリー([WAS_HOME])の下、libフォルダ内のJarファイルws-commons-logging.jarの中に含まれていることがわかります。

このプロパティーファイルの内容はこのようになっています:


                
org.apache.commons.logging.LogFactory=com.ibm.ws.commons.logging.TrLogFactory
                                                            

commons-loggingのログファクトリーの実装として、TrLogFactoryが選択されたのは、このためです。WebSphereでは「TrLogFactory」をcommons-loggingのロギング実装として使用するように強制的に指定されているのです。
これは、上位クラスローダー配下においてある「クラスやリソース」のため、下位クラスローダー配下のアプリケーションが影響を受けてしまう典型的な例です。

先ほどエラーメッセージに出ていた

1.LogFactoryの実装として「com.ibm.ws.commons.logging.TrLogLogFactory」が選択された

これは原因が理解できました。
それでは、もうひとつ

2.選択されたTrLogFactoryはLogFactoryのサブクラスではない

これはなぜでしょうか?

前回、解説したドッペルゲンガーがここでも登場しているのです。クラスローダーの観点から、この現象を見てみましょう(図1)。


図1 ドッペルゲンガー再び
図1

WebSphere EXTクラスローダー配下([WAS_HOME]/lib)には、commons-loggingのAPI(commons-logging-api.jar)と、そのWebSphere版の独自実装ws-commons-logging.jarが標準で付属しています。

その結果として、TrLogFactoryが継承するのは

(A)(WebSphere EXTクラスローダーがロードした)
org.apache.commons.logging.LogFactory

になります。

一方、クラスローダーのデリゲーションモードが「親が最後(PARENT LAST)」の場合は、Webアプリケーションから最初に見つかるLogFactoryは、自アプリケーション内(WEB-INF/lib)にいれておいた

(B)(WARクラスローダーがロードした)
org.apache.commons.logging.LogFactory

になります。

TrLogFactoryは(A)を継承しています。(B)にキャストすることはできません。これがClassCastExceptionの原因です。

「WebSphereでは独自にcommons-loggingの実装を提供しているため、アプリケーション内ではcommons-loggingを使用できない」という誤った認識が広がる結果になりました。




上に戻る


ディフェンシブ・パッケージング

このように上位クラスローダー配下に「何か」があっただけで、アプリケーションが影響を受けて動かなくなるのでは困ります。アプリケーションの上位クラスローダーに何がおいてあるか・その環境はアプリケーション・サーバーによって大きく異なりますし、同じアプリケーション・サーバーであってもバージョンによって異なります。不完全な他のアプリケーションを動作させるために、アプリケーション・サーバー管理者が「好ましくない」ライブラリーを、全アプリケーションから見える位置においていることもありえます。
アプリケーション作成側としては、自アプリケーション内に防衛策をとる必要があります。




上に戻る


ディフェンス - WARレベル

今回、例としてとりあげたcommons-loggingの場合は、対策は容易です。最も簡単な方法は、アプリケーション内にcommons-logging.propertiesを用意しておくことです。
例として、commons-loggingとLog4jと組み合わせる場合の、Webアプリケーションパッケージング例をリスト4に示します。


リスト4 commons-loggingとLog4jの組み合わせ
                
luv.war
  - WEB-INF/classes
      - commons-logging.properties
      - log4j.xml
  - WEB-INF/lib
      - commons-logging.jar
      - log4j.jar


commons-logging.propertiesには、以下のように明示的にLog4jによる実装を指定しておきます


                
org.apache.commons.logging.LogFactory=
org.apache.commons.logging.impl.Log4jFactory


(※実際は一行)

Log4jではなく、java.util.loggingパッケージを使用する場合は、以下のようになるでしょう。

                
org.apache.commons.logging.LogFactory=
org.apache.commons.logging.impl.Jdk14Logger


(※実際は一行)




上に戻る


ディフェンス-EARレベル

WAR単体ではなく、EAR全体でひとつのcommons-loggingを使用する場合はこのようになります。


リスト5 EAR全体でcommons-logging + Log4jを使用する
                
luv-app.ear
   - commons-logging.jar
   - log4j.jar
   - utility.jar
       - commons-logging.properties
       - log4j.xml
   - ejb1.jar
   - luv1.war
   - luv2.war


クラスローダーの観点から見た場合は以下のようになります。


図2 クラスローダーの観点から見た場合の図
図2

commons-logging.propertiesファイルやLog4Jの設定ファイルは、全J2EEモジュールから共通に見える箇所、すなわちアプリケーションクラスローダー配下に配置します。たとえば、ユーティリティーJARの中などがその候補になります。
こうしておけば、ひとつの設定ファイルで全モジュールのロギングの設定を行えるようになります。




上に戻る


まとめ

今日のオープンソースを最大限に活用するアプリケーション開発においては、「WEB-INF/libにおいてあるjarファイルがいつの間にか30個を超えていた」というのも決して珍しい状況ではありません。オープンソースライブラリーは、アプリケーションだけが使用するものではなく、アプリケーション・サーバー側でも活用しているケースが非常に多いです。

これらのライブラリー間の依存関係を正確に把握することは、パッケージング戦略において最重要項目のひとつです。しかし、実行時にクラスが見つかるかどうか・利用できるかどうかによって、動作を変更するライブラリーが多いことが正確な把握を一層困難にします。

残念ながら、これらの「JAR地獄(JAR HELL)」を解決する銀の弾丸は存在しません。クラスローダーに関する知識を武器に、地道にディフェンスあるのみです。

次回、最終回となる、「クラスローダーとJ2EEパッケージング戦略を理解する - 第5回 To Divide, Or Not?」では、最初にコンテキスト・クラスローダーについて解説します。その後、これまでに筆者が遭遇した現実のプロジェクトで採用されているJ2EEパッケージング戦略を実例としてとりあげて解説します。これまではひとつのEAR内での話が主でしたが、最終回では現実での運用を考えた上でのEAR分割戦略、WAR分割戦略、モジュール分割戦略とその評価をしていきます。



参考文献



著者について

著者である夷藤氏は、現在IBMにおいて、WebSphere Application Serverの技術支援を担当しており、多くのJ2EEプロジェクトにおいてシステムデザインやアプリケーション開発の助言を行っています。また、業界標準パフォーマンス評価団体、 SPECにおけるJ2EEアプリケーション・サーバー評価システム、 SpecJAppServer2002の開発を行っていました。

SPEC (US)
SpecJAppServer2002 (US)

専門はJava/J2EEですが、彼の興味はサーバーサイドのみならずクライアントサイド・テクノロジー、Python、Eclipseなどへと多岐に渡っており、雑誌「Eclipseパーフェクトマニュアル」でのテストファースト・プログラミングに関する記事執筆や、Eclipse - RCPJava/J2EEに関する講演活動などでもおなじみです。

Eclipse - RCP
Java/J2EE




記事の評価


サイト改善のため、ご意見をお寄せください。こちらのフォームからお願いいたします。



 


 


不充分・不完全である大変素晴らしい
 


この記事を共有する

del.icio.us del.icio.us newsing newsing FC2ブックマーク FC2ブックマーク
Choix! Choix! ニフティクリップ ニフティクリップ Yahoo!ブックマーク Yahoo!ブックマーク
MM/memo MM/memo CZブックマーク CZブックマーク livedoorクリップ livedoorクリップ
はてなブックマーク はてなブックマーク Buzzurl(バザール) Buzzurl(バザール)




上に戻る


    日本IBMについて プライバシー お問い合わせ