このチュートリアルの構成は、次のとおりです。
JavaTM プラットフォームのもっとも重要な機能の 1 つとして、通常は異なる物理システム上で稼動する別のプロセスで実行中の Java 仮想マシン* (JVM) に、任意の Uniform Resource Locator (URL) から Java ソフトウェアを動的にダウンロードする機能があります。この結果リモートシステムは、そのシステムのディスク上にインストールされていないアプレットなどのプログラムを実行できます。このドキュメントの最初の部分では、アプレットに関連したコードベースについて説明し、Java Remote Method Invocation (RMI) に関連したコードベースについて解説します。
たとえば、Web ブラウザの内部から実行されている JVM は、java.applet.Applet
のサブクラス、およびそのアプレットが必要とするその他のクラスのバイトコードをダウンロードできます。ブラウザが動作中のシステムは多くの場合、このアプレットを以前に実行したことも、ディスク上にインストールしたこともありません。必要なすべてのクラスがサーバからダウンロードされると、ブラウザは、このクライアントブラウザが実行されているシステムのローカルリソースを使って、アプレットプログラムの実行を開始できます。
Java RMI は、この機能を利用して、該当するクラスがディスクにインストールされたことのないシステム上にそのクラスをダウンロードし、実行します。 RMI API を使うと、ブラウザ内の JVM だけでなく、任意の JVM が、特別な RMI スタブクラスを含む任意の Java クラスをダウンロードできます。 こうして、リモートサーバ上でサーバシステムのリソースを使ってメソッド呼び出しを実行することが可能になります。
コードベースの考え方は、Java プログラミング言語での ClassLoader
の使用に基づいています。Java プログラムが ClassLoader
を使用する場合、そのクラスローダは、クラスをどの位置からロードできるかを知る必要があります。通常、クラスローダは、Java プラットフォームにコンパイル済みのクラスを提供する HTTP サーバと共に使われます。 多くの場合、ユーザが最初に扱った ClassLoader
とコードベースのペアは、AppletClassLoader
と、<applet>
HTML タグの「codebase」部分のペアでした。そのため、このチュートリアルでは、ユーザに Java RMI のプログラミング経験だけでなく、アプレットタグを含む HTML ファイルを記述した経験があることを想定しています。たとえば、HTML ソースには次のようなコードが含まれます。
<applet height=100 width=100 codebase="myclasses/" code="My.class"> <param name="ticker"> </applet>
コードベースとは、Java 仮想マシンにクラスをロードする際のソース、つまり位置であると定義できます。たとえば、新しい友達を夕食に招待する場合、あなたの家までの道順をその友達に伝える必要があるでしょう。 そうすれば、友達はあなたの家を見つけることができます。同じように、コードベースの位置を、JVM に指示すると考えることができます。 そうしておけば、JVM は「リモート可能」クラスを見つけることができます。
CLASSPATH
は、ローカルクラスのロード元となるディスク上の位置のリストであるため、「ローカルコードベース」であると考えることができます。ローカルディスクをベースにしたソースからクラスをロードする場合は、CLASSPATH
変数が参考にされます。CLASSPATH
は、クラスファイルのディレクトリまたはアーカイブ、あるいはその両方までの相対パスと絶対パスのどちらをとるようにも設定できます。CLASSPATH
が「ローカルコードベース」の一種であるのと同じく、アプレットおよびリモートオブジェクトによって使われるコードベースは、「リモートコードベース」であると考えることができます。
アプレットと対話を行うには、リモートクライアントは、そのアプレットと、アプレットが実行する必要のあるすべてのクラスにアクセスできる必要があります。アプレットは、「ftp://
」やローカルの「file:///
」 URL からのアクセスも可能ですが、通常は、リモート HTTP サーバからアクセスされます。
CLASSPATH
に存在しないアプレットクラスを要求する図 1: アプレットのダウンロード
アプレットのコードベースは、<applet>
タグが含まれる HTML ページの URL と常に関連しています。
RMI を使うと、アプリケーションは、ほかの JVM でクライアントからのメソッド呼び出しを受け入れるリモートオブジェクトを作成できます。クライアントがリモートオブジェクトのメソッドを呼び出すには、クライアントにリモートオブジェクトと通信する手段が必要です。RMI では、クライアントがリモートオブジェクトのプロトコルで通信を行うようにプログラミングするのではなく、スタブという特別なクラスを使用します。 スタブは、リモートオブジェクトとの通信に使用する (リモートオブジェクトに対してメソッド呼び出しを行う) クライアントにダウンロードできます。java.rmi.server.codebase
プロパティの値は、これらのスタブ (およびスタブが必要とするすべてのクラス) をダウンロードできる 1 つまたは複数の URL 位置を表します。
アプレットと同様、リモートメソッド呼び出しの実行に必要なクラスは「file:///
」URL からダウンロードできます。 ただし、アプレットの場合と同じく、URL によって参照されるファイルシステムが NFS などの別のプロトコルを使って利用可能になる場合を除き、「file:///
」URL を使うには、一般的にクライアントとサーバが同じ物理ホスト上に存在する必要があります。
一般に、リモートメソッド呼び出しを実行するために必要なクラスは、HTTP または FTP サーバなどのネットワークリソースからアクセスできるようにする必要があります。
図 2: RMI スタブのダウンロード
java.rmi.server.codebase
プロパティを設定することによって指定されます。RMI サーバは、名前にバインドされたリモートオブジェクトを RMI レジストリに登録します。サーバ JVM 上で設定されたコードベースには、RMI レジストリ内でリモートオブジェクト参照が注釈として付けられます。
CLASSPATH
内でローカルに検出できる場合は、クライアントはそのクラスをローカルにロードします (CLASSPATH
は、コードベースでの検索前に常に検索される)。スタブのクラス定義がクライアントの CLASSPATH
内で検出されなかった場合は、クライアントはリモートオブジェクトのコードベースからクラス定義を取得しようとします。
注: 手順 4 および 5 は、リモートオブジェクトが RMI レジストリで (RMI レジストリに登録された) 名前にバインドされたときに、レジストリがリモートオブジェクトクラスをロードするためにとった手順と同じです。レジストリがリモートオブジェクトのスタブクラスをロードしようとしたときは、レジストリはそのリモートオブジェクトに関連付けられたコードベースからクラス定義を要求しました。
図 3: リモートメソッド呼び出しを行う RMI クライアント
java.rmi.server.codebase
プロパティは、スタブとスタブに関連するクラスをクライアントにダウンロードすることのほかにも、スタブだけでなく任意のクラスのダウンロード元となる位置を指定するために使うことができます。
クライアントがリモートオブジェクトに対してメソッド呼び出しを行う場合、呼び出すメソッドが引数を受け取らないか、または複数の引数を受け取るように記述されていることがあります。メソッドの引数のデータ型に従って、3 つの異なるケースが考えられます。
第 1 のケースでは、メソッドのパラメータ (および戻り値) すべてが基本型であり、リモートオブジェクトがそれらをメソッドのパラメータに変換する方法を知っているため、CLASSPATH
またはコードベースをチェックする必要がありません。
第 2 のケースでは、リモートメソッドのパラメータまたは戻り値の少なくとも 1 つがオブジェクトであり、そのオブジェクトについて、リモートオブジェクトが CLASSPATH
内でローカルにクラス定義を検出できます。
第 3 のケースでは、(図 4 の手順 6 で示されているように)、リモートメソッドがオブジェクトインスタンスを受け取り、そのオブジェクトインスタンスについて、リモートオブジェクトが CLASSPATH
内でローカルにクラス定義を検出できません。この種のリモートメソッド呼び出しを、図 4 に示します。クライアントによって送信されるオブジェクトのクラスは、宣言されたパラメータの型のサブタイプになります。サブタイプは、次のどちらかです。
図 4: 未知のサブタイプをメソッドのパラメータとして渡してリモートメソッド呼び出しを行う RMI クライアント
7. アプレットのコードベースと同様に、Remote
クラス、非リモートクラス、およびほかの JVM へのインタフェースをダウンロードするには、クライアントによって指定されたコードベースを使います。codebase
プロパティがクライアントアプリケーション上で設定されている場合は、サブタイプのクラスがクライアントによってロードされるときに、コードベースにサブタイプインスタンスの注釈が付きます。コードベースがクライアント上で設定されていない場合は、リモートオブジェクトは誤って独自のコードベースを使ってしまいます。
アプレットの場合、このチュートリアルの最初の項で示した HTML の例のように、アプレットのコードベースの値は HTML ページに組み込まれます。
Java RMI コードベースの場合は、HTML ページにクラスへの参照を組み込むのではなく、クライアントはまず、RMI レジストリに問い合わせてリモートオブジェクトへの参照を要求します。リモートオブジェクトのコードベースは、既知の URL に関連した URL だけでなく任意の URL を参照できるので、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"
RMI スタブを含むすべての直列化可能クラスは、RMI プログラムが正しく設定されていればダウンロードできます。スタブの動的なダウンロードが可能であるための条件を次に示します。
bind
または rebind
への呼び出しを行うサーバプログラム (または、起動の場合は「セットアップ」プログラム) 上で、java.rmi.server.codebase
プロパティが次のように設定されている
codebase
プロパティの値が手順 A の URL である
および
codebase
プロパティの値として指定される URL がディレクトリの場合、末尾が「/」である
rmiregistry
が、スタブクラスまたはスタブクラスが必要とするクラスを CLASSPATH
内で検出できない。このため、サーバまたはセットアップコード内の bind
または rebind
への呼び出しの結果として、レジストリがスタブのクラスロードを行うときに、コードベースにスタブの注釈が付けられている
SecurityManager
がクライアントにインストールされている。つまり、Java 2 SDK, Standard Edition, v1.2 以降のバージョンでは、クライアントではセキュリティポリシーファイルが適切に設定されている必要がある
java.rmi.server.codebase
に関連した一般的な問題が 2 つあります。 次にこれらの問題について説明します。
まず最初に直面する可能性のある問題は、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
次に直面する可能性がある問題は、lookup
によってレジストリ内でリモートオブジェクトを検索しようとするときに、ClassNotFoundException
が返されるというものです。RMI クライアントコードを実行しようとした結果、スタックトレース内でこの例外が返された場合は、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)
RMI ユーザのメーリングリストに参加することができます。
このチュートリアルが有用な情報源となることを願っています。コメントや提案をお寄せください。その際は、タイトルを「codebase tutorial」として、rmi-comments@java.sun.com までお送りください。 * この Web サイトで使用されている用語「Java 仮想マシン」または「JVM」は、Java プラットフォーム用の仮想マシンを表します。
Copyright © 2001 Sun Microsystems, Inc. All Rights Reserved. コメントの送付先: rmi-comments@java.sun.com |