SHOEISHA iD

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

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

japan.internet.com翻訳記事

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

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

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

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

 コンポーネントコンテナフレームワークという概念は、jarまたはzipアーカイブで定義されたコンポーネントとそれらのコンポーネントが必要とする補助クラスをロードする役割を持ったコンテナエンティティとして実装できます。このフレームワークの目的は次のとおりです。

  1. インスタンス化するコンポーネントのバージョンを開発者が明確に指定できるようにすること
  2. 各コンポーネント用の適切な補助クラスを、コンポーネントと同じjarファイル内の情報に基づいて正しくロードすること
  3. 補助クラスとアーカイブをコンポーネント全体で共有すること

 コンポーネントとそれに関連する補助ファイルを定義するには、次のような設定ファイルが必要です。

<?xml version="1.0"?>
<component name=
   "com.jeffhanson.components.HelloWorld">
   <component-archive>
      HelloWorldComponentV1.jar
   </component-archive>

   <ancillary-resources>
      <ancillary-resource>
         log4j-1.2.12.jar
      </ancillary-resource>

      <ancillary-resource>
         concurrent-1.3.4.jar
      </ancillary-resource>
   </ancillary-resources>
</component>

 設定ファイルをもう1つ紹介します。上の例と、次の例の要素を比べてみてください。変更されている唯一の点は、component-archive要素の値だけです。それぞれのバージョンのコンポーネントを格納するアーカイブの名前は、この要素の値で定義します。

<?xml version="1.0"?>
<component name=
   "com.jeffhanson.components.HelloWorld">
   <component-archive>
      HelloWorldComponentV2.jar
   </component-archive>

   <ancillary-resources>
      <ancillary-resource>
         log4j-1.2.12.jar
      </ancillary-resource>

      <ancillary-resource>
         concurrent-1.3.4.jar
      </ancillary-resource>
   </ancillary-resources>
</component>

 フレームワークがクラスをロードする際に、指定した場所からのみロードするよう保証するためには、URLClassLoaderを拡張して新しいクラスローダを作成する必要があります。まず、loadClassメソッドをオーバーライドして、そのメソッドへの呼び出しが既定のクラスローダの親に伝播されることを防止します(これは結果的に、標準のクラスパスからのロードを防止することにつながります)。これにより、クラスローダに指定したURLでのみクラス検索が行われるようになるので、後はコンポーネントのロード元となる特定のjarファイルの場所をクラスローダに与えればよいことになります。

 次のコードは、この制限付きクラスローダのメカニズムを示しています。

package com.jeffhanson.components;

import java.net.URL;
import java.net.URLClassLoader;

public class RestrictedURLClassLoader
   extends URLClassLoader
{
  public RestrictedURLClassLoader(
     URL[] urls)
  {
     super(urls, null);
  }

  public Class loadClass(String name)
     throws ClassNotFoundException
  {
     Class cls = super.loadClass(name);

     if (cls == null)
     {
        throw new ClassNotFoundException(
           "Restricted ClassLoader" +
            " is unable to find class: " +
            name);
     }

     return cls;
  }
}

 この制限付きクラスローダは、コンポーネントコンテナがコンポーネントや補助クラスをロードする際に使われます。

 コンポーネントコンテナは、現在のスレッドのコンテキストクラスローダを利用して、目的のコンポーネントのURLを見つけます。その後、このURLを制限付きクラスローダに与え、コンポーネントのインスタンス作成に使用します。さらに、このコンポーネントクラスを以後の呼び出しに備えてキャッシュします。リスト1にコンポーネントコンテナのコードを示し、図2にコンポーネントコンテナフレームワークの各クラス間の関係を示します。

リスト1 コンポーネントコンテナクラスのコード
package com.jeffhanson.components;

import org.apache.commons.configuration.
   ConfigurationException;
import org.apache.commons.configuration.
   XMLConfiguration;
import org.apache.commons.configuration.Configuration;

import org.apache.log4j.Logger;

import java.net.URL;
import java.util.*;
import java.io.IOException;

public class ComponentContainer
{
   // ===============================================
   // static fields
   // ===============================================

   private static Logger log =
      Logger.getLogger(ComponentContainer.class);

   // ===============================================
   // member fields
   // ===============================================

   private String componentArchive = "";
   private URL[] ancillaryClassPathURLs = null;
   private HashMap componentCache = new HashMap();

   // ===============================================
   // constructors
   // ===============================================

   /**
    * Constructs a new ComponentContainer for
    * components named by the specified component
    * names. The names are assumed to
    * refer to JAR/zip files which will be downloaded
    * and opened as needed.
    *
    * @param componentConfigFileName   the name of the
    *                                  configuration
    *                                  file for the
    *                                  component from
    *                                  which to
    *                                  load classes
    *                                  and resources
    */
   public ComponentContainer(String
      componentConfigFileName)
      throws ConfigurationException
   {
      XMLConfiguration config =
         new XMLConfiguration(
         componentConfigFileName);
      this.componentArchive =
         config.getString("component-archive");
      Configuration subConfig =
         config.subset("ancillary-resources");
      if (subConfig != null)
      {
         String[] resNameArr =
            subConfig.getStringArray(
               "ancillary-resource");
         if (resNameArr != null &&
            resNameArr.length > 0)
         {
            this.ancillaryClassPathURLs =
               new URL[resNameArr.length];
            for (int i = 0; i < resNameArr.length;
               i++)
            {
               this.ancillaryClassPathURLs[i] =
                  Thread.currentThread().
                     getContextClassLoader().
                        getResource(resNameArr[i]);
            }
         }
      }
   }

   /**
    * Constructs a new ComponentContainer for
    * components named by the specified component
    * names. The names are assumed to refer to JAR/zip
    * files which will be downloaded and opened
    * as needed.
    *
    * @param componentArchive   the name of the
    *                           archive from which
    *                           to load the component,
    *                           classes and resources
    */
   public ComponentContainer(String componentArchive,
      URL[] ancillaryClassPathURLs)
   {
      this.componentArchive = componentArchive;

      if (ancillaryClassPathURLs != null &&
          ancillaryClassPathURLs.length > 0)
      {
         this.ancillaryClassPathURLs =
            new URL[ancillaryClassPathURLs.length];
         System.arraycopy(ancillaryClassPathURLs, 0,
            this.ancillaryClassPathURLs, 0,
            ancillaryClassPathURLs.length);
      }
   }

   // ==============================================
   // non-public methods
   // ==============================================

   protected Class findLoadedComponent(String name)
   {
      if (componentCache.get(name) != null)
      {
         return (Class)componentCache.get(name);
      }

      return null;
   }

   /**
    * Finds a component by name. This method will be
    * invoked by the loadComponent method.
    *
    * @param name   The name of the component
    * @return The component's <tt>Class</tt> object
    * @throws ComponentNotFoundException   If the
    *                                      component
    *                                      could not
    *                                      be found
    */
   protected Class findComponent(String name)
      throws ComponentNotFoundException
   {
      ClassLoader ctxClsLoader =
         Thread.currentThread().
         getContextClassLoader();

      URL url = ctxClsLoader.getResource(
         componentArchive);
      if (url != null)
      {
         URL[] urls = null;
         if (ancillaryClassPathURLs != null &&
             ancillaryClassPathURLs.length > 0)
         {
            urls = new URL[
               ancillaryClassPathURLs.length + 1];
            System.arraycopy(ancillaryClassPathURLs,
               0, urls, 0,
               ancillaryClassPathURLs.length);
            urls[ancillaryClassPathURLs.length] = url;
         }
         else
         {
            urls = new URL[1];
            urls[0] = url;
         }

         RestrictedURLClassLoader urlClassLoader =
            new RestrictedURLClassLoader(urls);

         try
         {
            Class cls = urlClassLoader.loadClass(
               name);
            if (cls != null)
            {
               return cls;
            }
         }
         catch (ClassNotFoundException e)
         {
            // ignore
         }
      }

      throw new ComponentNotFoundException(name);
   }

   // ===============================================
   // public methods
   // ===============================================

   /**
    * Loads the component with the specified name.
    *
    * @param name   The name of the component
    * @return The component <tt>Class</tt> object
    * @throws ComponentNotFoundException   If the
    *                                      component
    *                                      was not
    *                                      found
    */
   public Class loadComponent(String name)
      throws ComponentNotFoundException
   {
      Class cls = findLoadedComponent(name);
      if (cls != null)
      {
         return cls;
      }

      cls = findComponent(name);

      if (cls != null)
      {
         componentCache.put(name, cls);
         return cls;
      }

      throw new ComponentNotFoundException(
         "Definition for " + "component: " +
         name + " was not found");
   }
   /**
    * Loads and instantiates the component with the
    * specified name.
    *
    * @param componentName
    * @return The component <tt>Class</tt> object
    * @throws ComponentNotFoundException
    */
   public Object createComponent(String componentName)
      throws ComponentNotFoundException
   {
      Class cls = loadComponent(componentName);

      try
      {
         Object componentObj = cls.newInstance();
         return componentObj;
      }
      catch (Exception e)
      {
         throw new ComponentNotFoundException(
            "Unable to" + " instantiate " +
            " component: " + componentName);
      }
   }
}
図2 コンポーネントコンテナフレームワークのクラスの関係
図2 コンポーネントコンテナフレームワークのクラスの関係

次のページ
特定のクラスバージョンのロード

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

  • 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」など、さまざまなカンファレンスを企画・運営しています。

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

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

メールバックナンバー

アクセスランキング

アクセスランキング