Java

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

JDK 1.2 目次

動機

アプレット、Beans、アプリケーション、およびライブラリには多くの場合、リソースファイルが関連付けられています。アプレットや Beans などがどこにあるかには関係なく、これらのリソースファイルを使用できることが望まれます。

JDK 1.0.x の既存のコードでは、2 つのタイプの機構を使用します。1 つはアプレットが使用する機構です。Applet.getCodeBase() は、そのアプレットのコードのベースへの URL を返します。このベースを相対パスにより拡張して目的のリソースを指すようにすれば、そのリソースをロードすることができます (Applet.getAudioClip(url) などを使用)。もう 1 つは、アプリケーションが使用する機構です。アプリケーションは、「既知の位置」(System.getProperty("user.home")System.getProperty("java.home") など) を使用します。 この位置に "/lib/" を追加して、ファイルを開きます。

従来の JDK1.0.x には、コードから独立しているリソースの位置を特定する機構はありませんでした。つまり JDK1.0.x には、複数の http 接続を使ってネットワークからロードされたアプレット、JAR ファイルを使ってロードされたアプレット、ロードされた Beans、CLASSPATH 上にインストールされた Beans、または CLASPATH 上にインストールされたライブラリなどのリソースの位置を特定する手段がありませんでした。ここで説明する API は、このための機構を提供します。

国際化 API は、ResourceBundle の位置を特定するためのプリミティブオペレーションとして、この API を使用します。詳細については、国際化についての最新のドキュメントを参照してください。

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

リソースは、String によって識別されます。この String は空白の場合もありますが、/ で区切られたサブ文字列のシーケンスからなり、個々のサブ文字列は JavaTM の有効な識別子とそれに続く名前です。名前は、"<shortName>" または "<shortName>.<extension>" の形式です。"shortName" と "extension" はどちらも Java の有効な文字と数字で構成されます (JLS のセクション 3.8 を参照)。追加のシーケンスがある場合は、/ によって "shortName" と区切られます。

リソースの名前は、Java の実装とは無関係です。特に、区切り文字としては必ず / が使用されます。ただし、実際のリソースが入るファイルまたはデータベースのオブジェクトに、リソースの内容がどのようにマッピングされるかは、Java の実装が管理します。

リソース名の解釈は、ClassLoader のインスタンスに対する相対的な解釈です。ClassLoader が実装するメソッドが、この解釈を行います。

システムリソース

システムリソースは、システムクラスに似ています (JLS のセクション 20.14.5 を参照)。システムリソースは、システムに組み込まれているか、ホストの実装によってローカルファイルシステムなどに保持されているリソースです。システムリソースは、ベースのホスト実装に問い合わせをする特別なメソッド (getSystemResourcegetSystemResourceAsStream) を通してアクセスされます。

たとえば、ある実装において、システムリソースの位置を特定するために、CLASPATH 内のエントリを検索しなければならない場合があります。CLASSPATH 内のディレクトリ、ZIP ファイル、 または JAR ファイルのそれぞれのエントリでリソースファイルが検索され、ファイルが見つかると、InputStream またはリソースの名前が返されます。ファイルが見つからなかった場合は、null が返されます。リソースは、クラスファイルがロードされた場所ではない、CLASSPATH 内の別のエントリで見つかる場合もあります。

システムリソース以外のリソース

ある ClassLoader 上の getResource の実装は、ClassLoader の詳細により異なります。たとえば、 AppletClassLoader は次のように動作します。

すべての ClassLoader は、クラスファイルの検索に似た方法で、リソースファイルをシステムリソースとして検索します。この検索規則では、リソースをローカルで上書きすることが許可されます。クライアントは、(接頭辞として会社名やパッケージ名を使うことなどにより) 将来的にも一意であるようなリソース名を選択する必要があります。

リソース名

クラスが使用するリソースの名前には、「クラスのパッケージの完全修飾名を使用し、すべての '.' を '/' に変換し、"<Name>.<ext>" という形式のリソース名を追加する」という一般規約を適用します。これをサポートしつつ、システムクラス (getClassLoadernull を返す) の詳細を簡単に取り扱うために、Class クラスには ClassLoader 内の適切なメソッドを呼び出すための便利なメソッドが 2 つあります。

Class のメソッドに与えられるリソース名が '/' で始まる場合は、そのリソース名は「絶対」名です。'/' で始まらないリソース名は、「相対」名です。

絶対リソース名は、先頭の '/' を外してから、それ以外の修正を加えずに適切な ClassLoader のメソッドに渡され、リソースの位置が特定されます。相対リソース名は、上記の規約に従って修正されたあと、ClassLoader のメソッドに渡されます。

リソースの操作

getResource() メソッドはリソースの URL を返します。URL (およびその表現) は、実装と JVM のインスタンスごとに異なり (ある JVM インスタンスで取得される URL は、別の JVM のインスタンスでは機能しない)、実装の詳細によっても異なります (JDK 1.1 と JDK 1.1.1 の間でも異なる)。プロトコルは通常、そのリソースをロードする ClassLoader によって異なります。リソースが存在しない場合は、null が返されます。セキュリティ上の考慮によりリソースが隠されている場合にも、null が返されます。

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

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

Class に追加された API

Class クラスのメソッドの仕様は、次の形式です。


class Class {

    /**
     * Find a resource with a given name.  Will return null if no
     * resource with this name is found.  The rules for searching a
     * resources associated with a given class are implemented by the
     * ClassLoader of the class.
     *
     * The Class methods delegate to ClassLoader methods, after applying
     * a naming convention: if the resource name starts with "/", it is used
     * as is.  Otherwise, the name of the package is prepended, after
     * converting "." to "/".
     *
     * @see java.lang.ClassLoader
     */
    public InputStream getResourceAsStream(String name) {
	name = resolveName(name);
	ClassLoader cl = getClassLoader();
	if (cl==null) {
	    // A system class.
	    return ClassLoader.getSystemResourceAsStream(name);
	}
	return cl.getResourceAsStream(name);
    }

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

    /**
     * Add a package name prefix if the name is not absolute
     * Remove leading "/" if name is absolute
     */
    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;
    }



あまり一般的ではありませんが、同じリソースを共有しつつ、2 つの別々のパッケージに 2 つのクラスを持たせることは可能です。

ClassLoader に追加された API

リソースにアクセスするために、2 組のメソッドを用意しました。リソースの InputStream を返すメソッドの組と、URL を返すメソッドの組です。InputStream を返すメソッドは、使い方が簡単で、多くの場合はこの方法で十分です。一方、URL を返すメソッドでは、画像やオーディオクリップなどの複雑な情報へのアクセスができます。

リソースは、クラスの管理と似た方法で、 ClassLoader によって管理されます。ClassLoader はリソースの名前と内容のマッピングの方法を管理します。ClassLoader はまた、システムクラスへのアクセスと似た、システムリソースへのアクセス用のメソッドを提供します (JDK 1.0.x と JDK 1.1 ではシステムクラスは ClassLoader を持たない)。Class クラスには、適切な ClassLoader のメソッドに機能を委譲する便利なメソッドがあります。

多くの Java プログラムは、国際化 API を通じて間接的にこれらのメソッドにアクセスします。それ以外の Java プログラムは、Class クラス内のメソッドを通じてこれらのメソッドにアクセスします。ClassLoader のメソッドを直接呼び出すプログラムは、ごく少数です。

ClassLoader 内のメソッドは、与えられた String をリソースの名前として使用します。絶対名または相対名の変換は行いません (Class 内のメソッドを参照)。名前の先頭には、'/' が付いていてはいけません。


class ClassLoader {

    /**
     * A resource is some data (images, audio, text, etc) that wants to be
     * accessed by some class code in a way that is independent of the
     * location of the code.  Resources are found with cooperation of the
     * class loaders, since they are the only ones who know where the class
     * actually came from. 

* * System resources are those that are handled by the host implemenation * directly. For example, they may be located in the CLASSPATH.

* * The name of a resource is a "/"-separated sequence of identifiers. * The class Class provides convenience methods for accessing resources; * the methods implement a convention where the package name is prefixed * to the short name of the resource.

* * Resources can be accessed as an InputStream, or as a URL. * * @see Class */ /** * Get an InputStream on a given resource.. Will return null if no * resource with this name is found.

* * The resource name may be any system resource (e.g. follows CLASSPATH order) * @param name the name of the resource, to be used as is. * @return an InputStream on the resource, or null if not found. */ public static final InputStream getSystemResourceAsStream(String name) { ... this is equivalent to getSystemResource() call plus a openStream() } /** * Find a resource with a given name. The return is a URL to the resource * Doing a getContent() on the URL may return an ImageProducer, an AudioClip, or * an InputStream.

* * The resource name may be any system resource (e.g. follows CLASSPATH order) * @param name the name of the resource, to be used as is. * @return the URL on the resource, or null if not found. */ public static final java.net.URL getSystemResource(String name) { ... } /** */ /** * Get an InputStream on a given resource. Will return null if no * resource with this name is found.

* * The class loader can choose what to do to locate the resource. * @param name the name of the resource, to be used as is. * @return an InputStream on the resource, or null if not found. */ public InputStream getResourceAsStream(String name) { return null; } /** * Find a resource with a given name. The return is a URL to the resource. * Doing a getContent() on the URL may return an ImageProducer, an AudioClip, * or an InputStream.

* * The class loader can choose what to do to locate the resource. * @param name the name of the resource, to be used as is. * @return an InputStream on the resource, or null if not found. */ public java.net.URL getResource(String name) { return null; } }

クライアントコード

次に、クライアントコードの例を 2 つ示します。最初の例では絶対リソース名を使用し、従来の機構を使って 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.
    }


次の例では相対リソース名を使用し、 -experimental フラグによりコンパイラから利用可能な新しい機構を使用して Class クラスのオブジェクトを取得します。


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.
    }


セキュリティの詳細

getResource() は情報へのアクセスを提供するため、この機構が目的通りに使用されるようにサポートするセキュリティ規則を、正確に定義して、確立する必要があります。次に、JDK のバージョン 1.1.5 以降のリリースでの仕様と実装に従って、詳細を正確に説明します。セマンティクスは ClassLoader.getResourceClassLoader.getSystemResource() だけについて説明しますが、前のセクションの定義に従って AsStream メソッドに拡張されます。

セキュリティ上の考慮によって、あるリソースがあるセキュリティコンテキスト上で隠されている場合には、そのリソースがまったく存在しない場合と同じように、getResource() メソッドは失敗します (null を返す)。これにより、存在するリソースへの攻撃に対処します。

すべてのクラスローダが .class ファイルの内容へのアクセスを提供しているわけではありません。これは、セキュリティとパフォーマンスの両方の問題です。.class ファイルへの URL が得られるかどうかは、場合によります。

システムクラスローダ以外のクラスローダで探したリソースに関しては、セキュリティの問題や制限は指定されていません。AppletClassLoader は、JAR ファイルを通じて同じ位置から個別にまたはグループでロードされた、情報へのアクセス方法を提供します。したがって、AppletClassLoader は、getResource() を通じて URL を扱うときには、常に同じ checkConnect() 規則を適用する必要があります。

システムクラスローダは、CLASSPATH 内の情報へのアクセスを提供します。CLASSPATH には、ディレクトリと JAR ファイルのどちらか、またはその両方が含まれます。意図的に作成される JAR ファイルと、結果が偶然に左右されやすいディレクトリとは特性が異なっています。このため、特に、ディレクトリからの情報の取得に関しては、JAR ファイルからの取得よりも厳しい制限を与えました。

リソースがディレクトリにある場合

リソースが JAR ファイルにある場合

関連項目と既知のバグ

getResource インタフェースでは、地域対応されたリソースの位置を特定することはできません。地域対応されたリソースは、国際化によってサポートされます。

getResource のバグについては、classes_java を参照してください。


Copyright © 1995-97 Sun Microsystems, Inc. All Rights Reserved.

コメントの送付先: Eduardo Pelegrí-Llopart (pelegri@eng.sun.com).
最終更新日: 1994 年 11 月 29 日
Sun

Java ソフトウェア