JavaTM RMI 入門 |
ドキュメントの目次 |
このチュートリアルでは、おなじみの Hello World プログラムの分散システム版を JavaTM Remote Method Invocation (Java RMI) を使って作成する手順を説明します。このチュートリアルを学習するうちに、関連する多くの疑問に直面することでしょう。Java RMI FAQ および RMI-USERS メーリングリストのアーカイブには、さまざまな疑問に対する回答があります。RMI-USERS メーリングリストを購読するには、ここをクリックしてください。
ここで例として紹介する分散型の Hello World プログラムでは、単純なクライアントを使用して、リモートホスト上で稼動しているサーバへのリモートメソッド呼び出しを行います。クライアントでは、サーバから「Hello, world!」メッセージを受信します。
このチュートリアルでは、次の手順を実行します。
このチュートリアルの実行に必要なファイルは、次のとおりです。Hello.java
- リモートインタフェース
Server.java
- リモートインタフェースを実装するリモートオブジェクトの実装
Client.java
- リモートインタフェースのメソッドを呼び出す単純なクライアント
example.hello.Server
を指します。
リモートオブジェクトは、「リモートインタフェース」を実装するクラスのインスタンスです。リモートインタフェースはjava.rmi.Remote
インタフェースを拡張し、一組の「リモートメソッド」を宣言します。各「リモートメソッド」は、アプリケーション固有の例外のほかに、throws
節内でjava.rmi.RemoteException
(またはRemoteException
のスーパークラス) を宣言する必要があります。以下は、この例で使用される
examples.hello.Hello
リモートインタフェースの、インタフェース定義です。この例では、呼び出し側に文字列を返すsayHello
メソッドのみが宣言されています。リモートメソッド呼び出しでは、ネットワーク通信上の問題やサーバの問題など、ローカルメソッド呼び出しと比較して、多くの過程でエラーが発生する可能性があります。このためリモートメソッドは、package example.hello; import java.rmi.Remote; import java.rmi.RemoteException; public interface Hello extends Remote { String sayHello() throws RemoteException; }java.rmi.RemoteException
をスローすることによって、通信上の障害を報告します。分散システム上での障害および復元の詳細については、「A Note on Distributed Computing」を参照してください。
このチュートリアルでの「サーバ」クラスには、main
メソッドがあります。 このメソッドは、リモートオブジェクトの実装インスタンスを生成し、リモートオブジェクトをエクスポートして、そのインスタンスを Java RMI レジストリ内の名前にバインドします。このmain
メソッドを含むクラスは、実装クラスそのものである場合も、まったく別のクラスである場合もあります。この例のサーバ用の
次は、main
メソッドは、Hello
リモートインタフェースの実装も行うServer
クラス内に定義されています。 サーバのmain
メソッドで、次の操作が実行されます。Server
クラスのソースコードです。このサーバクラスを作成する際の説明は、ソースコードのあとにあります。package example.hello; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class Server implements Hello { public Server() {} public String sayHello( ) { return "Hello, world!"; } public static void main(String args[]) { try { Server obj = new Server(); Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0); // Bind the remote object's stub in the registry Registry registry = LocateRegistry.getRegistry(); registry.bind("Hello", stub); System.err.println("Server ready"); } catch (Exception e) { System.err.println("Server exception:" + e.toString()); e.printStackTrace( ); } } }
Server
実装クラスは、sayHello
リモートメソッドを実装して、Hello
リモートインタフェースを実装します。sayHello
メソッドでは、メソッドが例外をスローすることを宣言する必要はありません。メソッドの実装自体がRemoteException
やその他の検査済みのどのような例外もスローしないためです。注: クラスは、リモートインタフェースで指定されていないメソッドを定義できますが、これらのメソッドは、サービスを実行する仮想マシン内でしか呼び出すことはできず、リモートから呼び出すことはできません。
リモートオブジェクトを作成してエクスポートする
サーバのmain
メソッドでは、サービスを提供するリモートオブジェクトを作成する必要があります。さらに、着信するリモート呼び出しを受信できるように、リモートオブジェクトが Java RMI ランタイムへエクスポートされる必要があります。これは、次のようにして実行できます。Server obj = new Server(); Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);UnicastRemoteObject.exportObject
static メソッドは、指定されたリモートオブジェクトをエクスポートします。これにより、着信するリモートメソッド呼び出しを匿名 TCP ポートで受信し、そのリモートオブジェクトのスタブをクライアントへ渡すために返すことができるようになります。exportObject
呼び出しの結果として、ランタイムでは、リモートオブジェクトを求める着信リモート呼び出しを受け取るために、新規のサーバソケット上で待機を開始するか、共有サーバソケットを使うことができるようになります。返されたスタブには、リモートオブジェクトのクラスと同じリモートインタフェースのセットが実装され、リモートオブジェクトに連絡できるホスト名とポートが含まれています。注: J2SE 5.0 リリースでは、リモートオブジェクトが 5.0 VM より前を実行しているクライアントをサポートする必要がないかぎり、
rmic
スタブコンパイラを使用してリモートオブジェクトのスタブクラスを事前に生成しておく必要がなくなりました。使用しているアプリケーションがこのようなクライアントをサポートする必要がある場合は、そのアプリケーション内で使用されるリモートオブジェクトのスタブクラスを生成し、クライアントがダウンロードするようにスタブクラスを配置する必要があります。スタブクラスを生成する方法の詳細については、rmic
のツールドキュメント (Solaris 用、Windows 用) を参照してください。事前に生成したスタブクラス付きでアプリケーションを配置する方法の詳細については、Java RMI の使用による動的なコードのダウンロードに関するチュートリアルを参照してください。リモートオブジェクトを Java RMI レジストリに登録する
呼び出し側 (クライアント、ピア、またはアプレット) がリモートオブジェクトのメソッドを呼び出すために、呼び出し側は最初にリモートオブジェクトのスタブを取得する必要があります。ブートストラッピング用に、アプリケーションが名前をリモートオブジェクトのスタブにバインドし、クライアントがそのスタブを取得するためにリモートオブジェクトを名前で検索できるようにするために、Java RMI にはレジストリ API が用意されています。
Java RMI レジストリは単純なネームサービスであり、このサービスにより、クライアントはリモートオブジェクトへの参照 (スタブ) を取得することができます。通常、レジストリは、クライアントが使う必要がある最初のリモートオブジェクトの場所を特定するためにだけ使用されます。その後、通常はその最初のオブジェクトによって、その他のオブジェクトを検出するためのアプリケーション固有のサポートが提供されます。たとえば、参照は、別のリモートメソッド呼び出しに渡すパラメータ、またはリモートメソッド呼び出しからの戻り値として取得できます。詳細は、「Java RMI にファクトリパターンを適用する」を参照してください。
リモートオブジェクトがサーバに登録されると、呼び出し側は、そのオブジェクトを名前で検索して、リモートオブジェクトへの参照を取得できます。その後、そのオブジェクトのリモートメソッドを呼び出すことができます。
サーバ内の次のコードで、ローカルホストとデフォルトのレジストリポートのレジストリに対応するスタブを取得し、そのレジストリのスタブを使用して、「Hello」という名前をレジストリ内のリモートオブジェクトのスタブにバインドします。
引数をとらないRegistry registry = LocateRegistry.getRegistry(); registry.bind("Hello", stub);LocateRegistry.getRegistry
static メソッドは、java.rmi.registry.Registry
リモートインタフェースを実装するスタブを返し、デフォルトの1099
レジストリポート上のサーバにあるローカルホストのレジストリへ、呼び出しを送信します。次に、リモートオブジェクトのスタブをレジストリ内のHello
とバインドするために、bind
メソッドがregistry
スタブで呼び出されます。注:
LocateRegistry.getRegistry
の呼び出しでは、単純に、レジストリに対応する適切なスタブを返します。この呼出しでは、レジストリが実際に実行されているかどうかはチェックされません。bind
メソッドが呼び出されるときにローカルホストの TCP ポート 1099 でレジストリが実行されていない場合は、サーバがRemoteException
となり、失敗します。
クライアントプログラムでは、サーバのホスト上のレジストリに対応するスタブを取得し、レジストリ内の名前でリモートオブジェクトのスタブを検索し、その後そのスタブを使用して、リモートオブジェクトの
sayHello
メソッドを呼び出します。クライアントのソースコードは、次のとおりです。
package example.hello; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class Client { private Client() {} public static void main(String[] args) { String host = (args.length < 1) ? null :args[0]; try { Registry registry = LocateRegistry.getRegistry(host); Hello stub = (Hello) registry.lookup("Hello"); String response = stub.sayHello(); System.out.println("response:" + response); } catch (Exception e) { System.err.println("Client exception:" + e.toString()); e.printStackTrace( ); } } }このクライアントは、コマンド行に指定されたホスト名を使用して
LocateRegistry.getRegistry
static メソッドを呼び出すことで、レジストリに対応するスタブを最初に取得します。ホスト名が指定されていない場合は、ホスト名にnull
が使用され、ローカルホストのアドレスを使用する必要があることを指示されます。次にクライアントは、サーバのレジストリからリモートオブジェクトに対応するスタブを取得するために、レジストリスタブで
lookup
リモートメソッドを呼び出します。最後にクライアントは、リモートオブジェクトのスタブで
sayHello
メソッドを呼び出し、この呼び出しによって次の操作が発生します。
- クライアント側のランタイムは、リモートオブジェクトのスタブ内のホストおよびポート情報を使用してサーバへの接続を開き、呼び出しデータを直列化する
- サーバ側のランタイムは、着信する呼び出しを受け入れ、その呼び出しをリモートオブジェクトへディスパッチし、応答文字列の「Hello, world!」である結果をクライアントへ直列化する
- クライアント側のランタイムは、結果を受信し、直列化復元を行い、その結果を呼び出し側に返す
リモート・オブジェクトのリモート呼び出しから返される応答メッセージは、その後
System.out
に出力されます。
この例のソースファイルは、次のようにしてコンパイルできます。
destDir は、クラスファイルを配置するデスティネーションディレクトリです。javac -d destDir Hello.java Server.java Client.java注: 5.0 VM 以前で実行されるクライアントをサーバがサポートする必要がある場合は、
rmic
コンパイラを使用して事前にリモートオブジェクトの実装クラスに対応するスタブクラスを生成する必要があり、そのスタブクラスをクライアントがダウンロードできるようにする必要があります。詳細はJava RMI の使用による動的なコードのダウンロードに関するチュートリアルを参照してください。
この例を実行するには、次の操作を行う必要があります。
Java RMI レジストリを起動する
レジストリを起動するには、サーバのホストで
rmiregistry
コマンドを実行します。このコマンドからは成功時に何の出力もありません。通常、バックグラウンドで実行されます。詳細は、rmiregistry
のツールドキュメント (Solaris 用、Windows 用) を参照してください。この例の場合、Solaris (TM) オペレーティングシステムでは次のコマンドを実行します。
rmiregistry &Windows プラットフォームでは、次のコマンドを実行します。
start rmiregistryデフォルトでは、レジストリは TCP ポート番号 1099 で実行されます。別のポート上でレジストリを実行するには、コマンド行でポート番号を指定します。たとえば、Windows プラットフォーム上のポート 2001 でレジストリを起動するには、次のようにします。
start rmiregistry 2001レジストリが 1099 以外のポートで実行される場合は、
Server
およびClient
クラス内のLocateRegistry.getRegistry
の呼び出しに、ポート番号を指定する必要があります。たとえば、この例でレジストリをポート番号 2001 で実行する場合、サーバ内のgetRegistry
の呼び出しは次のようになります。Registry registry = LocateRegistry.getRegistry(2001);サーバを起動する
サーバを起動するには、次の
java
コマンドを使用してServer
クラスを実行します。Solaris オペレーティングシステムの場合:
java -classpath classDir example.hello.Server &Windows プラットフォームの場合:
start java -classpath classDir example.hello.ServerclassDir は、クラスファイルツリーのルートディレクトリです (「ソースファイルをコンパイルする」節の destDir を参照)。
サーバからの出力は、次のようになります。
Server readyユーザによって終了させられる (通常はプロセスの強制終了) まで、サーバは実行を続けます。
クライアントを実行する
サーバの準備ができると、次のようにしてクライアントを実行できます。
java -classpath classDir example.hello.ClientclassDir は、クラスファイルツリーのルートディレクトリです (「ソースファイルをコンパイルする」節の destDir を参照)。
クライアントから出力されるのは、次のメッセージです。
response:Hello, world!
Copyright © 2004 Sun Microsystems, Inc.All Rights Reserved. コメントの送付先: rmi-comments@java.sun.com |
? |