SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

Javaのクラスローディングを制御する

異なるバージョンのクラスを同時にロードして利用する

  • X ポスト
  • このエントリーをはてなブックマークに追加

Javaのクラスローディングを特定のjarファイルに限定するコンテナフレームワークを作成しておくと、常に適切なコンポーネントをランタイムにロードできるようになります。クラスのロード場所が限定されるので、異なるバージョンのクラスを同時にロードすることも可能です。

  • X ポスト
  • このエントリーをはてなブックマークに追加

はじめに

 Javaのクラスローディングフレームワークは、強力かつ柔軟です。このクラスローディングフレームワークを使えば、アプリケーションがクラスライブラリにアクセスする際に、静的な「インクルード」ファイルにリンクする必要がなくなります。その代わりに、指定の場所(例えばCLASSPATH環境変数で定義したディレクトリやネットワークロケーションなど)から、ライブラリクラスを格納したアーカイブファイルとリソースがロードされます。このシステムにより、実行時のクラスとリソースへの参照が動的に解決されるため、更新と改訂バージョンのリリース作業が簡略化されます。それでも、各ライブラリは独自の依存関係をひととおり持っており、アプリケーションが正しいバージョンを確実にうまく参照できるかどうかは、開発者と導入担当者次第です。あいにく、既定のクラスローディングシステムと特定の依存関係はバグやシステムクラッシュ、あるいはもっと悪い事態を引き起こす可能性があります(というよりも実際に引き起こしています)。

 本稿では、このような問題を解決するためのクラスローディングコンテナフレームワークを紹介します。

Javaのクラスパス

 Javaでは、ランタイムがクラスや他のリソースを必要に応じて検索する際のパスを指定するのに環境プロパティ/変数、それにCLASSPATHを使用します。CLASSPATHを適切に設定するには、CLASSPATH環境変数を設定するか、Javaのコマンドラインオプションである-classpathを使います。

 一般に、Javaのランタイムは次の順序でクラスを探し、ロードします。

  1. ブートストラップクラスのリストに記載されているクラス
  2. これらは、例えば「rt.jar」内のクラスのように、Javaプラットフォームを統合するクラスです。
  3. 拡張クラスのリストに記載されているクラス
  4. これらのクラスではJava、プラットフォームを拡張するExtension Mechanism Frameworkというフレームワークを、.jarや.zipなど、/lib/extランタイム環境ディレクトリにあるアーカイブファイルと共に使用します。
  5. ユーザークラス
  6. これらのクラスでは、-classpathコマンドラインオプションやCLASSPATH環境変数で識別される拡張メカニズムアーキテクチャを使いません。

アーカイブとクラスパス

 .jarや.zipなどのアーカイブファイルには、アーキテクチャに関する情報を提供したり、アーカイブのプロパティを設定したりするための「マニフェストファイル」を格納することができます。このマニフェストファイルでも、アーカイブのリストとディレクトリを格納するClass-Pathという名前のエントリを含めることによってクラスパスを拡張することができます。JDK 1.3では、必要に応じてオプションのjarやディレクトリを指定するエントリ用としてClass-Pathマニフェストが導入されました。Class-Pathエントリの例を次に示します。

Class-Path: mystuff/utils.jar
   mystuff/logging.jar mylib/

 Javaでは、クラスをロードするための場所やファイルのリストを指定できる、拡張可能なモデルが用意されています。しかし、「そのクラスパスに存在するライブラリが、実行クラスが期待しているものとはバージョンが違う」という問題が発生する可能性があります。

クラスパスバージョンの競合

 JavaのランタイムIDは完全修飾名(クラス名の前にパッケージ名を付加したもの)で定義され、それらはすべて、そのクラスをロードしたクラスローダのIDの後ろに付加されます。従って、複数のクラスローダによってロードされたインスタンスは、Javaランタイムからは別々のエンティティと見なされます。つまり、ランタイムは同じクラスのいくつかのバージョンを任意のタイミングでロードできるということです。これは非常に強力で柔軟な機能ですが、賢く使わなければ、その副作用に悩まされる可能性があります。

 例えば、同じようなセマンティクスを持つ複数のソース(例えばファイルシステムとデータベース)のデータにアクセスするエンタープライズアプリケーションを開発している場面を想像してください。この種のアプリケーションは多くの場合、類似のデータソースを抽象化するDAO(Data Access Object)を使ってデータアクセス層を公開するという方法を採用しています。さらに、DAOクライアントの新機能についての要望に応えるために、APIをわずかに変更した新しいバージョンのデータベースDAOをロードしたとします。ただし、まだ新しいAPIに対応できていない他のクライアントのために、古いDAOも残しておく必要があります。一般的なランタイム環境では、古いDAOが新しいバージョンのDAOで単純に置き換えられてしまい、新しいインスタンスはすべて新しいバージョンから作成されることになります。しかし、ランタイム環境を止めないままで更新を行った場合(ホットローディング)は、古いDAOに基づく既存のインスタンスと、新しいDAOから作成されたインスタンスがメモリ内に共存することになります。この点はどう考えても混乱の元です。さらに困るのは、あるDAOクライアントが、古いバージョンのDAOのインスタンスが作成されることを期待しているにもかかわらず、実際に取得したのはAPI変更後の新しいバージョンのインスタンスだった、という危険が生じることです。このように、いろいろと厄介な問題が生じる可能性があります。

 安定性と安全性を確保するために、呼び出し側のコードは、使用したいクラスの正確なバージョンを「指名」できる必要があります。この問題に対処するには、クラスローディングメカニズムとコンポーネントコンテナモデルを作成し、いくつかのシンプルなクラスローディングテクニックを使用します。

アーカイブとコンポーネント

 アーカイブファイル(jarファイル、zipファイルなど)は、Javaのクラスローディングメカニズムや開発ツールと密接に結び付いているため、自己定義コンポーネントの「入れ物」として利用するのにちょうどよい候補となります。Javaコンポーネントをアーカイブの中にパッケージ化してデプロイするという処理がうまくいっているのは、次の条件が成立しているからです。

  • インスタンス化するコンポーネントのバージョンを開発者が明確に指定できる。
  • コンポーネントの補助クラスの適切なバージョンを、コンポーネントと同じjarファイル内の情報に基づいて正しくロードできる。

 これにより、どのバージョンのコンポーネントを実際に作成して使用するかをコンポーネントの開発者と使用者が完全に制御できます。

 以降では、コンポーネントとコンポーネントの名前空間を、どのアーカイブに格納するかによって定義するという考え方について説明します。

補助リソースの共有

 標準クロスローダを使ってJavaの共有ライブラリを扱う場合の最大の問題は、すべてのクラスが単一の名前空間にロードされてしまうことです。そのせいで、同じライブラリの別々のバージョンを任意のタイミングで使い分けるのは、非常に困難です。コンポーネントや補助ライブラリのロード先となる独自の名前空間を、開発者が自分で定義できる必要があります。

 JavaのクラスのランタイムIDはクラスの完全修飾名とクラスローダのIDによって定義されるので、個々のクラスローダには既に名前空間があります。従って、そのクラスローダを利用して、コンポーネント(およびその依存コンポーネント)の名前空間を定義するコンポーネントコンテナを作成することができます。

 例えば、「com.jeffhanson.components.HelloWorld」という名前のクラスがあり、このクラスを2種類のバージョンで動かしたいとします。この場合の解決策は、それぞれのバージョンを別々のクラスローダで作成することです。この概念を図1に示します。

図1 複数のクロスローダの使用:Javaの命名規則の仕組みにより、別々のクロスローダを使うと別々の名前空間が定義される
図1 複数のクロスローダの使用:Javaの命名規則の仕組みにより、別々のクロスローダを使うと別々の名前空間が定義される

 後で例を紹介しますが、1つのクラスを2つの異なるクラスローダでインスタンス化するというテクニックでは、実際には1つの仮想名前空間が作成されます。ただし本稿の例では、同じバージョンのクラスのインスタンスを複数作成しただけです。

 同じクラスの複数のバージョンをロードしてインスタンス化する処理を容易にするために、以降では、クラスローダの名前空間メカニズムに基づいて同じクラスの別々のバージョンをロードできるようにしたコンポーネントコンテナフレームワークの例を紹介します。

次のページ
クラスローダの名前空間を利用する

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Jeff Hanson(Jeff Hanson)

ソフトウェア業界で18年以上の経験を持つ。これまでにWindows OpenDocポートの上級エンジニア、NovellのRoute 66フレームワークの主席設計者を務める。現在は、J2EEベースの再保険システム用のフレームワークとプラットフォームの提供を専門とするeReinsureの主任設計者。著書、執筆記事多数。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/487 2006/08/22 15:51

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング