RMIとは、Remote Method Invocationの略で、別のホストのJavaオブジェクトのメソッドを呼び出すための通信手段です。 RMIを使うと、通信を意識せずに、Javaのサーバ、クライアントプログラムを作成することができます。
RMIでは、クライアント側にスタブモジュール(Stub)を用意し、サーバ側にスケルトンモジュール(Skel)を用意する必要があります。
これらのモジュールは、rmicコマンドで作成できます。
動作の流れとしては、クライアントがサーバのメソッドを呼び出すと、スタブのリモートインターフェイスが、メソッドの引数をネットワークのストリームに変換してくれます。
このストリームを、サーバ側のスケルトンが受け取り、サーバのリモートオブジェクトのメソッドを呼び出します。
また、返却値は、サーバのリモートオブジェクトから、スケルトン、スタブによって、クライアントに戻されます。
+-----------------+ +-----------------+ | +-----------+ | | +-----------+ | | | Client | | | | Server | | | +-----------+ | | +-----------+ | | 返却値↑ | | ↑メソッド| | │メソッド| | │ 引数 | | ↓ 引数 | | 返却値↓ | | +-----------+ | ストリーム | +-----------+ | | | Stub |<------------------>| Skel | | | +-----------+ | | +-----------+ | +-----------------+ +-----------------+ |
RMIの名前解決には、RMIレジストリが使われます。 RMIレジストリには、bindメソッドで登録し、lookupメソッドで参照します。 RMIレジストリを再起動すると、登録した名前は消去されます。 また、名前を再登録する場合は、rebindを使います。
+-----------------+ +-----------------+ | +-----------+ | | +-----------+ | | | Client | | | | Server | | | +-----------+ | | +-----------+ | | ↓参照 | | ↓登録 | | +-----------+ | | +-----------+ | | | lookup() | | | | bind() | | | +-----------+ | | +-----------+ | | │ | | ↓ | | │ | | +-----------+ | | └------------------------->|rmiregistry| | | | | +-----------+ | +-----------------+ +-----------------+ |
RMIのクラスを使うため、java.rmi.*をインポートします。
import java.rmi.*; |
まず、クライアントが参照するリモートインターフェイスを作成します。 インターフェイスは、メソッドの宣言だけで、処理の実体は持ちません。
helloIf.java |
---|
import java.rmi.*; public interface helloIf extends Remote { public String helloMsg() throws RemoteException; } |
リモートインターフェイスをもとに、サーバ側のリモートオブジェクトを作成します。
リモートオブジェクトは、リモートインターフェイスをimplementsしていますので、同じ引数のメソッドを持つことになります。
RMIのメソッドは、必ず、throws RemoteExceptionを設定する必要があります。
サンプル(helloObj.java)では、「Hello World!」を返却するメソッド(helloMsg)を作成しています。
helloObj.java |
---|
import java.rmi.*; import java.rmi.server.*; public class helloObj extends UnicastRemoteObject implements helloIf { public helloObj() throws RemoteException { } public String helloMsg() throws RemoteException { return "Hello World!"; } } |
サーバプログラム(helloServer.java)を作成します。
RMIでは、セキュリティマネージャを使わない場合は通信できません。
リモートオブジェクトを作成し、bindメソッドでレジストリサーバに登録します。
レジストリサーバを再起動せずに、何回も登録する場合は、rebindメソッドを使います。
helloServer.java |
---|
import java.rmi.*; public class helloServer { public static void main(String args[]) { helloObj hello = null; try { // セキュリティマネージャの設定 System.setSecurityManager(new RMISecurityManager()); // リモートオブジェクトの作成 hello = new helloObj(); // レジストリサーバに登録(再登録) Naming.bind("//localhost/hello", hello); // Naming.rebind("//localhost/hello", hello); } catch(Exception ex) { ex.printStackTrace(); } } } |
クライアントプログラム(helloClient.java)を作成します。
レジストリサーバから、lookupメソッドでリモートインターフェイスを取得し、「Hello World!」を返却するメソッド(helloMsg)を呼び出します。
helloClient.java |
---|
import java.rmi.*; public class helloClient { public static void main(String args[]) { helloIf hello = null; try { // セキュリティマネージャの設定 System.setSecurityManager(new RMISecurityManager()); // レジストリサーバの検索 hello = (helloIf)Naming.lookup("rmi://localhost/hello"); } catch(Exception ex) { ex.printStackTrace(); } try { // メッセージの取得 System.out.println(hello.helloMsg()); } catch(Exception ex) { ex.printStackTrace(); } } } |
javacで、4つのソースファイルをコンパイルします。
unix# javac *.java unix# ls *.class helloObj.class helloIf.class helloServer.class helloClient.class |
rmicで、リモートオブジェクトをコンパイルします。
コンパイルすると、スタブ(*_Stub.class)とスケルトン(*_Skel.class)が作成されます。
スタブクラスは、リモートオブジェクトと同じメソッドを持ち、スケルトンに中継します。
また、スケルトンクラスは、ストリームから引数を取り出し、サーバのリモートオブジェクトに中継します。
unix# rmic helloObj unix# ls helloObj_* helloObj_Stub.class helloObj_Skel.class |
ローカルマシン上でテストする場合は、ポリシーファイル(java.policy)は必要ありません。 リモートホストを使う場合は、RMIによるクラスのダウンロードを参照してください。
RMIレジストリサーバを起動します。UNIX系の場合とWindows系の場合で起動方法が異なります。
・UNIX系の場合 unix# rmiregistry & ・Windows系の場合 win# start rmiregistry |
RMIレジストリサーバを起動したら、サーバプログラムを起動します。
unix# java helloServer |
クライアントプログラムを起動し、「Hello World!」と表示されれば、成功です。
unix# java helloClient Hello World! |