位置に依存しない方法でのリソースへのアクセス

概要

リソースとは、プログラムのコードの位置とは無関係な方法でプログラムがアクセスする必要のあるデータ (イメージ、オーディオ、テキストなど) です。Java プログラムでは、2 つの機構でリソースにアクセスできます。アプレットの場合は、Applet.getCodeBase() を使用してアプレットコードのベース URL を取得し、そのベース URL を相対パスで展開し、たとえば Applet.getAudioClip(url) などの関数を使用して必要なリソースをロードします。アプリケーションの場合は、System.getProperty("user.home")System.getProperty("java.home") などの「よく知られた場所」を使用し、「/lib/resource」を付加して、そのファイルを開きます。

クラス Class および ClassLoader のメソッドでは、リソースを特定するために、位置に依存しない方法を提供します。たとえば次のような場合のリソースを特定できます。

これらのメソッドでは、地域対応されたリソースの位置を特定することはできません。地域対応されたリソースは、国際化によってサポートされます。

リソース、名前、コンテキスト

リソースは文字列で特定され、この文字列はスラッシュ (/) で区切られた一連のサブ文字列と、末尾のリソース名で構成されます。各サブ文字列は、有効な Java 識別子である必要があります。リソース名は、shortName または shortName.extension という形式です。shortNameextension は、どちらも Java 識別子である必要があります。

リソースの名前は Java の実装には依存しません。特に、パスの区切り文字は必ずスラッシュ (/) です。ただし、リソースの内容が、ファイル、データベース、その他実際のリソースを含むオブジェクトにどのようにマップされるかについては、Java の実装が制御します。

リソース名の解釈は、クラスローダのインスタンスと関連します。ClassLoader クラスによって実装されるメソッドがこの解釈を行います。

システムリソース

システムリソースとは、システムに組み込まれているリソース、またはホストの実装に組み込まれているリソースのことで、ローカルファイルシステムがその一例です。プログラムがシステムリソースにアクセスするには、ClassLoader のメソッドである getSystemResource および getSystemResourceAsStream を使用します。

たとえば、一部の実装機能では、システムリソースを見つけるために CLASSPATH 内の項目を検索しなければならないことがあります。ClassLoader メソッドによって CLASSPATH 内のディレクトリ、ZIP ファイル、 または JAR ファイルのそれぞれのエントリでリソースファイルが検索され、ファイルが見つかると、InputStream またはリソースの名前が返されます。ファイルが見つからなかった場合は、null が返されます。リソースは、クラスファイルをロードした位置と同じ CLASSPATH 項目にあるとは限りません。

システムリソース以外

クラスローダでの getResource の実装方法は、ClassLoader クラスの詳細によって異なります。AppletClassLoader の場合は次のようになります。

すべてのクラスローダは、クラスファイルを探すのと同じように、リソースをまずシステムリソースとして探します。このため、すべてのリソースをローカルに上書きすることが可能になります。リソース名には一意の名前を選択する必要があります (接頭辞として会社名やパッケージ名などを使用する)。

リソース名

各クラスは一般的に、リソース名を表す際に、クラスのパッケージの完全修飾名を使用し、すべてのピリオド (.) をスラッシュ (/) に変換し、リソース名を name.extension の形式で追加するという規則を使用します。この規則をサポートし、システムクラス (getClassLoadernull を返すクラス) の詳細の処理を簡略化するために、Class クラスには 2 つの便利なメソッドを提供します。

Class クラスのメソッドに渡されるリソース名は、先頭が「/」で始まるものがあります。これは「絶対」名を表します。先頭が「/」ではないリソース名は「相対」名です。

リソースを探す際、絶対名は、先頭の「/」が除去されただけの状態で、適切な ClassLoader のメソッドに渡されます。相対名は、前述の規則に従って修正されたあと、ClassLoader のメソッドに渡されます。

java.lang.Class のメソッドの使用

Class クラスでは、リソースをロードするためのメソッドをいくつか実装しています。

getResource() メソッドはリソースの URL を返します。この URL およびその表現は、実装および JVM に固有です。つまり、ある実行時インスタンスで取得された URL は、別の実行時インスタンスでは動作しない可能性があります。プロトコルは通常、リソースをロードする ClassLoader に特有です。リソースが存在しない場合や、セキュリティ上の理由で不可視である場合、メソッドは null を返します。

クライアントコードでリソースの内容を InputStream として読み込むには、その URL に openStream() メソッドを適用します。getResourceAsStream()ClassClassLoader に追加することは、通常は正しいこととされます。getResourceAsStream() は、入出力例外がキャッチされて null の InputStream を返されることを除いて、getResource().openStream() を呼び出すことと同一です。

クライアントコードは、URL に対して java.net.URL.getContent() メソッドを適用することによって、リソースの内容をオブジェクトとして要求することもできます。このメソッドは、リソース内にイメージデータが含まれている場合などに便利です。イメージの場合、結果は Image オブジェクトではなく、awt.image.ImageProducer オブジェクトになります。

getResource および getResourceAsStream メソッドは、指定された名前のリソースを検索します。これらのメソッドは、指定された名前のリソースが見つからないと null を返します。指定されたクラスに関連するリソースを検索するための規則は、そのクラスの ClassLoader によって実装されます。Class のメソッドは、命名規則の適用後に ClassLoader メソッドに委譲されます。リソース名が「/」で始まる場合は、その名前がそのまま使用されます。そうでない場合は、すべてのピリオド (.) がスラッシュ (/) に変換されてから、パッケージ名が先頭に付加されます。

public InputStream getResourceAsStream(String name) {
  name = resolveName(name);
  ClassLoader cl = getClassLoader();
  if (cl==null) {
    return ClassLoader.getSystemResourceAsStream(name); // A system class.
  }
  return cl.getResourceAsStream(name);
}

public java.net.URL getResource(String name) {
  name = resolveName(name);
  ClassLoader cl = getClassLoader();
  if (cl==null) {
    return ClassLoader.getSystemResource(name);  // A system class.
  }
  return cl.getResource(name);
}

resolveName メソッドによって、名前が絶対パスでない場合はパッケージ名の接頭辞が追加され、絶対パスの場合は先頭の「/」が削除されます。一般的ではありませんが、同じリソースを共有するクラスを別々のパッケージに含めることもできます。

private String resolveName(String name) {
  if (name == null) {
    return name;
  }
  if (!name.startsWith("/")) {
    Class c = this;
    while (c.isArray()) {
      c = c.getComponentType();
    }
    String baseName = c.getName();
    int index = baseName.lastIndexOf('.');
    if (index != -1) {
      name = baseName.substring(0, index).replace('.', '/') + "/" + name;
    }
  } else {
    name = name.substring(1);
  }
  return name;
}

java.lang.ClassLoader のメソッドの使用

ClassLoader クラスには、2 組のリソースにアクセスするメソッドがあります。このうちの一方は、リソースの InputStream を返します。もう一方は、URL を返します。InputStream を返す各メソッドは、使いやすく、用途も多くあります。もう一方の URL を返す各メソッドを使うと、Image オブジェクトや AudioClip オブジェクトなど、さらに複雑な情報にアクセスすることができます。

ClassLoader は、クラスを管理するのと同様の方法で、リソースを管理します。ClassLoader はリソース名とその内容のマップ方法を制御します。またシステムクラスの場合と同じように、ClassLoader には、システムリソースにアクセスするためのメソッドも提供します。Class クラスには、機能を ClassLoader クラスの各メソッドに任せるという便利なメソッドがあります。

多くの Java プログラムは、国際化 API を使用してこれらのメソッドに間接的にアクセスします。Class のメソッドを介してアクセスするプログラムもあります。ClassLoader クラスのメソッドを、直接呼び出すことはほとんどありません。

ClassLoader のメソッドは、受け取った String をリソース名として使用します。絶対名と相対名との変換は行いません (Class のメソッドを参照)。名前の先頭には「/」を付ける必要はありません。

システムリソースは、ホスト実装によって直接処理されるリソースです。たとえば、CLASSPATH で特定できる場合があります。

リソースの名前は、一連の識別子を「/」で区切った形式です。Class クラスでは、リソースへアクセスする簡易メソッドを提供しています。これらのメソッドでは、パッケージ名をリソースのショート名に接頭辞として追加するための規則を実装しています。

リソースは InputStream または URL としてアクセスできます。

getSystemResourceAsStream メソッドは、指定されたシステムリソースに対する InputStream を返します。そのリソースが見つからない場合は、null を返します。リソース名は、任意のシステムリソースです。

getSystemResource メソッドは、指定された名前を持つシステムリソースを検索します。このメソッドは、リソースへの URL を返します。リソースが見つからない場合は null を返します。その URL を使用して java.net.URL.getContent() を呼び出すと、ImageProducerAudioClipInputStream などのオブジェクトを返します。

getResourceAsStream メソッドは、指定されたリソースに対する InputStream を返します。そのリソースが見つからない場合は、null を返します。

getResource メソッドは、指定された名前を持つリソースを検索します。このメソッドは、リソースへの URL を返します。リソースが見つからない場合は null を返します。その URL を使用して java.net.URL.getContent() を呼び出すと、ImageProducerAudioClipInputStream などのオブジェクトを返します。

セキュリティ

getResource() は、情報へのアクセスを提供するため、セキュリティの規則が適切に定義および構築されている必要があります。セキュリティ上の配慮により、あるセキュリティコンテキストであるリソースへのアクセスが許可されていない場合は、getResource() メソッドは、あたかもそのリソースが存在しないかのように失敗する (null を返す) ようになっています。これは、存在性攻撃に対する配慮です。

クラスローダは、セキュリティおよびパフォーマンス上の理由から、.class ファイルの内容へのアクセスは提供しません。.class ファイルの URL を取得できるかどうかは、以下に示す詳細によって異なります。

システム以外のクラスローダによって検索されるリソースに関する、セキュリティの問題または制限は指定されていません。AppletClassLoader は、あるソースの場所からロードされた情報への個々のアクセスまたは JAR ファイルによるグループでのアクセスを提供します。このため、AppletClassLoader は、getResource() を使って複数の URL を扱うときは、同じ checkConnect() を適用する必要があります。

システムの ClassLoader は、CLASSPATH の情報へのアクセスを提供します。CLASSPATH には、ディレクトリや JAR ファイルがあります。JAR ファイルは意図的に作成されるので、意図せずに生成されるディレクトリとは重要性が異なるといえます。特に、ディレクトリから情報を取得する場合は、JAR ファイルから取得する場合よりも厳密に行います。

リソースがディレクトリ内にある場合は、以下のようになります。

リソースが JAR ファイル内にある場合は、以下のようになります。

このセクションでは、クライアントコードの例について説明します。1 番目の例では「絶対リソース」名と従来の機構を使用して Class クラスのオブジェクトを取得しています。

package pkg;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

class Test {

  private static final String absName = "/pkg/mumble.baf";

  public static void test1() {
    Class c=null;
    try {
      c = Class.forName("pkg.Test");
    } catch (Exception ex) {
      // This should not happen.
    }
    InputStream s = c.getResourceAsStream(absName);
    // do something with it.
  }

  public void test2() {
    InputStream s = this.getClass().getResourceAsStream(absName);
  // do something with it.
  }
}

この例では「相対リソース」名と機構を使用して Class のオブジェクトを取得しています。この機構は、コンパイル時に -experimental フラグを使用すると利用できます。

package pkg;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

class Test {
  private static final String relName = "mumble.baf";
  public static void test1() {
  InputStream s = Test.class.getResourceAsStream(relName);
  // do something with it.
}

  public void test2() {
    InputStream s = Test.class.getResourceAsStream(relName);
    // do something with it.
  }

API の参照先


Copyright © 1996-98 Sun Microsystems, Inc.All Rights Reserved.  Sun Microsystems, Inc
Java ソフトウェア