Java™ RMI の使用による動的なコードのダウンロード
(java.rmi.server.codebase プロパティーを使用)


このチュートリアルの構成は、次のとおりです。

  1. はじめに
  2. コードベースとは
  3. コードベースの動作
  4. スタブのダウンロード以外の目的で Java RMI でコードベースを使用する
  5. コマンド行の例
  6. トラブルシューティングのヒント

1.0 はじめに

Java™ プラットフォームのもっとも重要な機能の 1 つとして、通常は異なる物理システム上で稼働する別のプロセスで実行中の仮想マシン (VM) に、Uniform Resource Locator (URL) から Java Software を動的にダウンロードする機能があります。この結果リモートシステムは、そのシステムのディスク上にインストールされていないアプレットなどのプログラムを実行できます。このドキュメントの最初の数セクションでは、アプレットに関連したコードベースについて説明し、Java Remote Method Invocation (Java RMI) に関連したコードベースについて解説します。

たとえば、Web ブラウザの内部から実行されている VM は、java.applet.Applet のサブクラス、およびそのアプレットが必要とするその他のクラスのバイトコードをダウンロードできます。ブラウザが動作中のシステムは多くの場合、このアプレットを以前に実行したことも、ディスク上にインストールしたこともありません。必要なすべてのクラスがサーバーからダウンロードされると、ブラウザは、このクライアントブラウザが実行されているシステムのローカルリソースを使って、アプレットプログラムの実行を開始できます。

Java RMI は、この機能を利用して、該当するクラスがディスクにインストールされたことのないシステム上にそのクラスをダウンロードし、実行します。Java RMI API を使うと、ブラウザ内の VM だけでなく、任意の VM が、特別な Java RMI スタブクラスを含む任意の Java クラスをダウンロードできます。こうして、リモートサーバー上でサーバーシステムのリソースを使ってメソッド呼び出しを実行することが可能になります。

コードベースの考え方は、Java プログラミング言語での ClassLoader の使用に基づいています。Java プログラムが ClassLoader を使用する場合、そのクラスローダーは、クラスをどの位置からロードできるかを知る必要があります。通常、クラスローダーは、Java プラットフォームにコンパイル済みのクラスを提供する HTTP サーバーとともに使われます。多くの場合、ユーザーが最初に扱うようになる ClassLoader とコードベースのペアは、AppletClassLoader と、HTML <applet> タグの「codebase」部分のペアです。そのため、このチュートリアルでは、ユーザーに Java RMI のプログラミング経験だけでなく、アプレットタグを含む HTML ファイルを記述した経験があることを想定しています。たとえば、HTML ソースには次のようなコードが含まれます。

<applet height=100 width=100 codebase="myclasses/" code="My.class">
        <param name="ticker">
</applet>

2.0 コードベースとは

コードベースとは、仮想マシンにクラスをロードする際のソース、つまり位置であると定義できます。たとえば、新しい友達を夕食に招待する場合、あなたの家までの道順をその友達に伝える必要があるでしょう。そうすれば、友達はあなたの家を見つけることができます。同じように、コードベースの位置を、JVM に指示すると考えることができます。そうしておけば、JVM は「リモート可能」クラスを見つけることができます。

CLASSPATH は、ローカルクラスのロード元となるディスク上の位置のリストであるため、「ローカルコードベース」であると考えることができます。ローカルディスクをベースにしたソースからクラスをロードする場合は、CLASSPATH 変数が参考にされます。CLASSPATH は、クラスファイルのディレクトリまたはアーカイブ、あるいはその両方までの相対パスと絶対パスのどちらをとるようにも設定できます。CLASSPATH が「ローカルコードベース」の一種であるのと同じく、アプレットおよびリモートオブジェクトによって使われるコードベースは、「リモートコードベース」であると考えることができます。

3.0 コードベースの動作

3.1 アプレットでのコードベースの動作

アプレットと対話を行うには、リモートクライアントは、そのアプレットと、アプレットが実行する必要のあるすべてのクラスにアクセスできる必要があります。アプレットは、「ftp://」やローカルの「file:///」URL からのアクセスも可能ですが、通常は、リモート HTTP サーバーからアクセスされます。

  1. クライアントブラウザがクライアントの CLASSPATH に存在しないアプレットクラスを要求する
  2. アプレット (およびアプレットが必要とするその他のクラス) のクラス定義が、HTTP を使ってサーバーからクライアントにダウンロードされる
  3. クライアント上でアプレットが実行される
上記 3 つの手続きを図示する

図 1:アプレットのダウンロード

アプレットのコードベースは、<applet> タグが含まれる HTML ページの URL と常に関連しています。

3.2 Java RMI でのコードベースの動作

Java RMI を使うと、アプリケーションは、ほかの VM のクライアントからのメソッド呼び出しを受け入れるリモートオブジェクトを作成できます。クライアントがリモートオブジェクトのメソッドを呼び出すには、クライアントにリモートオブジェクトと通信する手段が必要です。Java RMI では、クライアントがリモートオブジェクトのプロトコルで通信を行うようにプログラミングするのではなく、スタブという特別なクラスを使用します。スタブは、リモートオブジェクトとの通信に使用する (リモートオブジェクトに対してメソッド呼び出しを行う) クライアントにダウンロードできます。java.rmi.server.codebase プロパティーの値は、これらのスタブ (およびスタブが必要とするすべてのクラス) をダウンロードできる 1 つまたは複数の URL 位置を表します。

アプレットと同様、リモートメソッド呼び出しの実行に必要なクラスは「file:///」URL からダウンロードできます。ただし、アプレットの場合と同じく、URL によって参照されるファイルシステムが NFS などの別のプロトコルを使って利用可能になる場合を除き、「file:///」URL を使うには、一般的にクライアントとサーバーが同じ物理ホスト上に存在する必要があります。

一般に、リモートメソッド呼び出しを実行するために必要なクラスは、HTTP または FTP サーバーなどのネットワークリソースからアクセスできるようにする必要があります。

下に示すとおり、スタブのダウンロードプロセスの最初の 5 ステップを図示する

図 2:Java RMI スタブのダウンロード

  1. リモートオブジェクトのコードベースは、リモートオブジェクトのサーバーが java.rmi.server.codebase プロパティーを設定することによって指定されます。Java RMI サーバーは、名前にバインドされたリモートオブジェクトを Java RMI レジストリに登録します。サーバー VM 上で設定されたコードベースには、Java RMI レジストリ内でリモートオブジェクト参照が注釈として付けられます。
  2. Java RMI クライアントは、名前付きのリモートオブジェクトへの参照を要求します。参照 (リモートオブジェクトのスタブインスタンス) は、クライアントがリモートオブジェクトに対してリモートメソッド呼び出しを行うために使うものです。
  3. Java RMI レジストリは、要求されたクラスに参照 (スタブインスタンス) を返します。スタブインスタンスのクラス定義がクライアントの CLASSPATH 内 (コードベースでの検索前に常に検索される) でローカルに検出できる場合は、クライアントはそのクラスをローカルにロードします。スタブのクラス定義がクライアントの CLASSPATH 内で検出されなかった場合は、クライアントはリモートオブジェクトのコードベースからクラス定義を取得しようとします。
  4. クライアントはコードベースからクラス定義を要求します。クライアントが使うコードベースは、スタブクラスがレジストリによってロードされたときにスタブインスタンスに注釈として付けられた URL です。ステップ 1 では、エクスポートされるオブジェクトに注釈として付けられたスタブはそののち、名前にバインドされた Java RMI レジストリに登録されます。
  5. スタブ (およびスタブが必要とするすべてのクラス) のクラス定義がクライアントにダウンロードされます。
    注: ステップ 4 および 5 は、リモートオブジェクトが Java RMI レジストリで (Java RMI レジストリに登録された) 名前にバインドされたときに、レジストリがリモートオブジェクトクラスをロードするためにとった手順と同じです。レジストリがリモートオブジェクトのスタブクラスをロードしようとしたときは、レジストリはそのリモートオブジェクトに関連付けられているコードベースからクラス定義を要求しました。
  6. クライアントはこれで、リモートオブジェクト上のリモートメソッドの呼び出しに必要なすべての情報を入手しました。スタブインスタンスが、サーバー上に存在するリモートオブジェクトに対してプロキシとして動作します。このため、Java RMI クライアントは、コードベースを使ってローカル VM でコードを実行するアプレットとは異なり、図 3 に示すように、リモートオブジェクトのコードベースを使って、別の、リモート可能な VM にあるコードを実行します。
上記手順の最終ステップを図示する

図 3:リモートメソッド呼び出しを行う Java RMI クライアント

4.0 スタブのダウンロード以外の目的で Java RMI でコードベースを使用する

java.rmi.server.codebase プロパティーは、スタブとスタブに関連するクラスをクライアントにダウンロードすることのほかにも、スタブだけでなく任意のクラスのダウンロード元となる位置を指定するために使うことができます。

クライアントがリモートオブジェクトに対してメソッド呼び出しを行う場合、呼び出すメソッドが引数を受け取らないか、または複数の引数を受け取るように記述されていることがあります。メソッドの引数のデータ型に従って、3 つの異なるケースが考えられます。

第 1 のケースでは、メソッドのパラメータ (および戻り値) すべてが基本型であり、リモートオブジェクトがそれらをメソッドのパラメータに変換する方法を知っているため、CLASSPATH またはコードベースをチェックする必要がありません。

第 2 のケースでは、リモートメソッドのパラメータまたは戻り値の少なくとも 1 つがオブジェクトであり、そのオブジェクトについて、リモートオブジェクトが CLASSPATH 内でローカルにクラス定義を検出できます。

第 3 のケースでは、(図 4 の手順 6 で示されているように)、リモートメソッドがオブジェクトインスタンスを受け取り、そのオブジェクトインスタンスについて、リモートオブジェクトが CLASSPATH 内でローカルにクラス定義を検出できません。この種のリモートメソッド呼び出しを、図 4 に示します。クライアントによって送信されるオブジェクトのクラスは、宣言されたパラメータの型のサブタイプになります。サブタイプは、次のどちらかです。

上または下に説明してあるメソッドパラメータとしての、未知のサブタイプの引き渡しの説明。

図 4:未知のサブタイプをメソッドのパラメータとして渡してリモートメソッド呼び出しを行う Java RMI クライアント

7. アプレットのコードベースと同様に、Remote クラス、非リモートクラス、およびほかの VM へのインタフェースをダウンロードするには、クライアントによって指定されたコードベースを使います。codebase プロパティーがクライアントアプリケーション上で設定されている場合は、サブタイプのクラスがクライアントによってロードされるときに、コードベースにサブタイプインスタンスの注釈が付きます。コードベースがクライアント上で設定されていない場合は、リモートオブジェクトは誤って独自のコードベースを使ってしまいます。

5.0 コマンド行の例

アプレットの場合、このチュートリアルの最初のセクションで示した HTML の例のように、アプレットのコードベースの値は HTML ページに組み込まれます。

Java RMI コードベースの場合は、HTML ページにクラスへの参照を組み込むのではなく、クライアントはまず、Java RMI レジストリに問い合わせてリモートオブジェクトへの参照を要求します。リモートオブジェクトのコードベースは、既知の URL に関連した URL だけでなく任意の URL を参照できるので、Java RMI コードベースの値は、スタブクラス、およびスタブクラスが必要とするその他のクラスの位置に対する絶対的な URL でなければなりません。この codebase プロパティーの値は、次のものを参照できます。

注:codebase プロパティーの値をディレクトリの URL に設定する場合は、値の末尾に「/」を付ける必要があります。

ダウンロード可能なクラスの位置が、「webvector」という名前の HTTP サーバー上のディレクトリ「export」(Web ルートの直下) にある場合は、codebase プロパティーの設定は次のようになります。

        -Djava.rmi.server.codebase=http://webvector/export/

ダウンロード可能なクラスの位置が、「webline」という名前の HTTP サーバー上のディレクトリ「public」(Web ルートの直下) 内の JAR ファイル「mystuff.jar」にある場合は、codebase プロパティーの設定は次のようになります。

        -Djava.rmi.server.codebase=http://webline/public/mystuff.jar

ここで、ダウンロード可能なクラスの位置が「myStuff.jar」と「myOtherStuff.jar」の 2 つの JAR ファイルに分割されている場合を想定します。これらの JAR ファイルがそれぞれ「webfront」と「webwave」という別々のサーバー上にある場合は、codebase プロパティーの設定は次のようになります。

        -Djava.rmi.server.codebase="http://webfront/myStuff.jar http://webwave/myOtherStuff.jar"

6.0 トラブルシューティングのヒント

Java RMI スタブを含むすべての直列化可能クラスは、Java RMI プログラムが正しく構成されていればダウンロードできます。スタブの動的なダウンロードが可能であるための条件を次に示します。

  1. スタブクラス、およびスタブが必要とするすべてのクラスがクライアントからアクセス可能な URL で提供されている。
  2. bind または rebind への呼び出しを行うサーバープログラム (または、起動の場合は「セットアップ」プログラム) 上で、java.rmi.server.codebase プロパティーが次のように設定されている
    • codebase プロパティーの値が手順 A の URL である

      および

    • codebase プロパティーの値として指定される URL がディレクトリの場合、末尾が「/」である

  3. rmiregistry が、スタブクラスまたはスタブクラスが必要とするクラスを CLASSPATH 内で検出できない。このため、サーバーまたはセットアップコード内の bind または rebind への呼び出しの結果として、レジストリがスタブのクラスロードを行うときに、コードベースにスタブの注釈が付けられている。
  4. スタブのダウンロードを可能にする SecurityManager がクライアントにインストールされている。つまり、Java 2 SDK, Standard Edition, v1.2 以降のバージョンでは、クライアントではセキュリティーポリシーファイルが適切に構成されている必要がある。

java.rmi.server.codebase に関連した一般的な問題が 2 つあります。次にこれらの問題について説明します。

6.1 Java RMI サーバーの実行で問題が発生する場合

最初に直面する可能性のある問題は、レジストリ内の名前にリモートオブジェクトを bind または rebind しようとするときに、ClassNotFoundException が返されるというものです。通常、この例外は、codebase プロパティーの設定が適切でないために、リモートオブジェクトのスタブまたはスタブが必要とするその他のクラスをレジストリが検索できないために発生します。

リモートオブジェクトのスタブは、リモートオブジェクト自体と同じインタフェースをすべて実装するため、メソッドのパラメータまたは戻り値として宣言されるほかのカスタムクラスと同様に、これらのインタフェースも指定されたコードベースからダウンロードできなければなりません。

この例外は、多くの場合、プロパティーの URL に末尾のスラッシュを付けなかったためにスローされます。ほかにも、プロパティーの値が URL ではない、URL に指定されたクラスへのパスが正しくないかスペルが間違っている、スタブクラスまたはほかに必要なクラスが指定された URL から利用できない、などの理由があります。

このような場合にスローされる例外を次に示します。

java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
        java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
        java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
        java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub
java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub
        at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Compiled Code)
        at sun.rmi.transport.StreamRemoteCall.executeCall(Compiled Code)
        at sun.rmi.server.UnicastRef.invoke(Compiled Code)
        at sun.rmi.registry.RegistryImpl_Stub.rebind(Compiled Code)
        at java.rmi.Naming.rebind(Compiled Code)
        at examples.callback.MessageReceiverImpl.main(Compiled Code)
RemoteException occurred in server thread; nested exception is:
        java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
        java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub

6.2 Java RMI クライアントの実行で問題が発生する場合

次に直面する可能性がある問題は、lookup によってレジストリ内でリモートオブジェクトを検索しようとするときに、ClassNotFoundException が返されるというものです。Java RMI クライアントコードを実行しようとした結果、スタックトレース内でこの例外が返された場合は、Java RMI レジストリが開始された CLASSPATH に問題があります。セクション 6.0 の条件 C を参照してください。この場合にスローされる例外を次に示します。

java.rmi.UnmarshalException: Return value class not found; nested exception is:
        java.lang.ClassNotFoundException: MyImpl_Stub
        at sun.rmi.registry.RegistryImpl_Stub.lookup(RegistryImpl_Stub.java:109
        at java.rmi.Naming.lookup(Naming.java:60)
        at RmiClient.main(MyClient.java:28)

その他のリソース

コードベースの問題が引き続き解決できない場合は、まず、River プロジェクトの外部にあるメーリングリストおよびドキュメントの保存を参照してください。


Copyright © 1993, 2013, Oracle and/or its affiliates. All rights reserved.