3.6. デプロイメントでのプログラムを用いたクラスローダーの使用

3.6.1. デプロイメントでのプログラムによるクラスおよびリソースのロード

プログラムを用いて、アプリケーションコードでクラスやリソースを検索またはロードできます。複数の要素に応じてその方法を選択します。ここでは、使用できる方法を説明し、それらの方法を使用するガイドラインを提供します。

Class.forName() メソッドを使用したクラスのロード

Class.forName() メソッドを使用すると、プログラムでクラスをロードおよび初期化できます。このメソッドには 2 つのシグネチャーがあります。

  • Class.forName(String className):

    このシグネチャーは、1 つのパラメーター (ロードする必要があるクラスの名前) のみを取ります。このメソッドシグネチャーを使用すると、現在のクラスのクラスローダーによってクラスがロードされ、デフォルトで新たにロードされたクラスが初期化されます。

  • Class.forName(String className, boolean initialize, ClassLoader loader):

    このシグネチャーは、クラス名、クラスを初期化するかどうかを指定するブール値、およびクラスをロードする ClassLoader の 3 つのパラメーターを想定します。

プログラムでクラスをロードする場合は、3 つの引数のシグネチャーを用いる方法が推奨されます。このシグネチャーを使用すると、ロード時に目的のクラスを初期化するかどうかを制御できます。また、JVM はコールスタックをチェックして、使用するクラスローダーを判断する必要がないため、クラスローダーの取得および提供がより効率的になります。コードが含まれるクラスの名前が CurrentClass である場合は、CurrentClass.class.getClassLoader() メソッドを使用してクラスのクラスローダーを取得できます。

以下は、ロードするクラスローダーを提供し、TargetClass クラスを初期化する例になります。

Class<?> targetClass = Class.forName("com.myorg.util.TargetClass", true, CurrentClass.class.getClassLoader());

名前ですべてのリソースを検索

リソースの名前とパスが分かり、直接そのリソースをロードする場合は、標準的な Java Development Kit (JDK) の Class または ClassLoader API を使用するのが最良の方法です。

  • 単一のリソースをロードします。

    ご使用のクラスと同じディレクトリーまたはデプロイメントの他のクラスと同じディレクトリーにある単一のリソースをロードする場合は、Class.getResourceAsStream() メソッドを使用できます。

    InputStream inputStream = CurrentClass.class.getResourceAsStream("targetResourceName");
  • 単一リソースのインスタンスをすべてロードします。

    デプロイメントのクラスローダーが認識できる単一リソースのすべてのインスタンスをロードするには、Class.getClassLoader().getResources(String resourceName) メソッドを使用します。ここで、resourceName はリソースの完全修飾パスに置き換えます。このメソッドは、指定の名前でクラスローダーがアクセスできるリソースに対し、すべての URL オブジェクトの列挙を返します。その後、URL の配列で繰り返し処理し、openStream() メソッドを使用して各ストリームを開くことができます。

    以下の例は、1 つのリソースのすべてのインスタンスをロードし、結果で処理を繰り返します。

    Enumeration<URL> urls = CurrentClass.class.getClassLoader().getResources("full/path/to/resource");
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        InputStream inputStream = null;
        try {
            inputStream = url.openStream();
            // Process the inputStream
            ...
        } catch(IOException ioException) {
            // Handle the error
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    // ignore
                }
            }
        }
    }
    注記

    URL インスタンスはローカルストレージからロードされるため、openConnection() や他の関連するメソッドを使用する必要はありません。ストリームは非常に簡単に使用でき、ストリームを使用することにより、コードの複雑さが最小限に抑えられます。

  • クラスローダーからクラスファイルをロードします。

    クラスがすでにロードされている場合は、以下の構文を使用して、そのクラスに対応するクラスファイルをロードできます。

    InputStream inputStream = CurrentClass.class.getResourceAsStream(TargetClass.class.getSimpleName() + ".class");

    クラスがロードされていない場合は、クラスローダーを使用し、パスを変換する必要があります。

    String className = "com.myorg.util.TargetClass"
    InputStream inputStream = CurrentClass.class.getClassLoader().getResourceAsStream(className.replace('.', '/') + ".class");

3.6.2. デプロイメントでのプログラムによるリソースの繰り返し

JBoss Modules ライブラリーは、すべてのデプロイメントリソースを繰り返し処理するために複数の API を提供します。JBoss Modules API の JavaDoc は http://docs.jboss.org/jbossmodules/1.3.0.Final/api/ にあります。これらの API を使用するには、以下の依存関係を MANIFEST.MF に追加する必要があります。

Dependencies: org.jboss.modules

これらの API により柔軟性が向上しますが、直接パスを検索するよりも動作がかなり遅くなることに注意してください。

ここでは、アプリケーションコードでプログラムを用いてリソースを繰り返す方法を説明します。

  • デプロイメント内およびすべてのインポート内のリソースをリストします。

    場合によっては、正確なパスでリソースを検索できないことがあります。たとえば、正確なパスがわからなかったり、指定のパスで複数のファイルをチェックする必要がある場合などです。このような場合、JBoss Modules ライブラリーはすべてのデプロイメントを繰り返し処理するための API を複数提供します。2 つのメソッドのいずれかを使用すると、デプロイメントでリソースを繰り返し処理できます。

    • 単一のモジュールで見つかったすべてのリソースを繰り返し処理します。

      ModuleClassLoader.iterateResources() メソッドは、このモジュールクラスローダー内のすべてのリソースを繰り返し処理します。このメソッドは、検索を開始するディレクトリーの名前と、サブディレクトリーで再帰的に処理するかどうかを指定するブール値の 2 つの引数を取ります。

      以下の例は、ModuleClassLoader の取得方法と、bin/ ディレクトリーにあるリソースのイテレーターの取得方法 (サブディレクトリーを再帰的に検索) を示しています。

      ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
      Iterator<Resource> mclResources = moduleClassLoader.iterateResources("bin",true);

      取得されたイテレーターは、一致した各リソースをチェックし、名前とサイズのクエリー (可能な場合) を行うために使用できます。また、読み取り可能ストリームを開いたり、リソースの URL を取得するために使用できます。

    • 単一のモジュールで見つかったすべてのリソースとインポートされたリソースを繰り返し処理します。

      Module.iterateResources() メソッドは、このモジュールクラスローダー内のすべてのリソース (モジュールにインポートされたリソースを含む) を繰り返し処理します。このメソッドは、前述のメソッドよりもはるかに大きなセットを返します。このメソッドには、特定パターンの結果を絞り込むフィルターとなる引数が必要になります。代わりに、PathFilters.acceptAll() を指定してセット全体を返すことも可能です。

      以下の例は、インポートを含む、このモジュールのリソースのセット全体を検索する方法を示しています。

      ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
      Module module = moduleClassLoader.getModule();
      Iterator<Resource> moduleResources = module.iterateResources(PathFilters.acceptAll());
  • パターンと一致するすべてのリソースを検索します。

    デプロイメント内またはデプロイメントの完全なインポートセット内で特定のリソースのみを見つける必要がある場合は、リソースの繰り返しをフィルターする必要があります。JBoss Modules のフィルター API は、リソースの繰り返しをフィルターする複数のツールを提供します。

    • 依存関係の完全セットを確認します。

      依存関係の完全なセットをチェックする必要がある場合は、Module.iterateResources() メソッドの PathFilter パラメーターを使用して、一致する各リソースの名前を確認できます。

    • デプロイメント依存関係を確認します。

      デプロイメント内のみを検索する必要がある場合は、ModuleClassLoader.iterateResources() メソッドを使用します。が、追加のメソッドを使用して結果となるイテレーターをフィルターする必要があります。PathFilters.filtered() メソッドは、リソースイテレーターのフィルターされたビューを提供できます。PathFilters クラスには、さまざまな関数を実行するフィルターを作成する多くの静的メソッドが含まれています。これには、子パスや完全一致の検索、Ant 形式の「glob」パターンの一致などが含まれます。

  • リソースのフィルターに関する追加のコード例。

    以下の例は、異なる基準を基にしてリソースをフィルターする方法を示しています。

    例: デプロイメントでファイル名が messages.properties のファイルをすべて検索

    ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
    Iterator<Resource> mclResources = PathFilters.filtered(PathFilters.match("**/messages.properties"), moduleClassLoader.iterateResources("", true));

    例: デプロイメントおよびインポートでファイル名が messages.properties のファイルをすべて検索

    ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
    Module module = moduleClassLoader.getModule();
    Iterator<Resource> moduleResources = module.iterateResources(PathFilters.match("**/message.properties"));

    例: デプロイメントでディレクトリー名が my-resources であるディレクトリー内部のファイルをすべて検索

    ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
    Iterator<Resource> mclResources = PathFilters.filtered(PathFilters.match("**/my-resources/**"), moduleClassLoader.iterateResources("", true));

    例: デプロイメントおよびインポートで messages または errors という名前のファイルをすべて検索

    ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
    Module module = moduleClassLoader.getModule();
    Iterator<Resource> moduleResources = module.iterateResources(PathFilters.any(PathFilters.match("**/messages"), PathFilters.match("**/errors"));

    例: デプロイメントで指定のパッケージにあるすべてのファイルを検索

    ModuleClassLoader moduleClassLoader = (ModuleClassLoader) TargetClass.class.getClassLoader();
    Iterator<Resource> mclResources = moduleClassLoader.iterateResources("path/form/of/packagename", false);