概要
RMI(Remote Method Invocation)は、Javaでリモートオブジェクトのメソッドを呼び出すための技術です。初期のJava学習段階でこの技術に触れた際は、ネットワークプログラミングの知識不足から理解が困難でした。現在ではネットワークおよびI/Oに関する知識が蓄積されたため、RMIの仕組みもより深く理解できるようになりました。
RMIの基本概念
Wikipediaによると、「Java RMIは、リモートメソッド呼び出しを実行するJava APIであり、遠隔プロシージャ呼び出し(RPC)のオブジェクト指向版として機能し、シリアル化可能なJavaクラスの直接転送と分散ガベージコレクションをサポートする」とされています。
この定義から以下の要点が読み取れます:
- Java特有のAPIである
- オブジェクト指向のRPC相当の機能を持つ
- シリアル化可能なクラスを直接転送可能
- 分散ガベージコレクションをサポート(詳細未調査)
リモート呼び出しとは、リモートのクラスをローカルにロードして実行するのではなく、メソッド引数をサーバーに渡し、サーバー側で処理を実行して結果を返す仕組みです。
ネットワーク通信とI/O
RMIによるリモート呼び出しは、異なるJVM間でのオブジェクトメソッド呼び出しプロセスです。これらのJVMは同一マシンまたは異なるホスト上に配置できます。異なるホストの場合、メソッド呼び出しにはネットワーク通信が必要となり、Socket通信とJRMIPプロトコルが利用されます。RMI APIの利点は、ユーザーが低レベルのネットワーク通信やI/O処理を実装する必要がないことです。
RMIの構成要素
Remoteインターフェース
java.rmi.serverパッケージに定義されたインターフェースで、リモートインターフェースはこのRemoteインターフェースを継承する必要があります。
RMIレジストリ
サービス登録用のレジストリサービスで、サーバーは対応するサービスをレジストリに登録します。クライアントはサーバーのIPアドレスでサーバーを特定し、レジストリから対応するスタブを検索します。
UnicastRemoteObject
リモートインターフェースの実装クラスはこのクラスを継承する必要があります。JRMPプロトコル関連の通信オブジェクトとスタブを生成するために使用されます。
サーバー側の実装手順
- リモートインターフェースの定義 - Remoteインターフェースを継承したインターフェースを定義し、メソッドはRemoteExceptionをスローするように宣言します。
- リモートインターフェースの実装 - 上記インターフェースを実装し、UnicastRemoteObjectを継承したクラスを作成します。Naming.rebindメソッドを使用してサービスをレジストリに登録します。
- RMIレジストリサービスの起動 - コマンドラインで%rmiregistryを実行します。
- リモートサービスの開始 - 登録処理を含むクラスを実行します。
クライアント側の実装手順
- リモートインターフェースの定義 - サーバー側と同じインターフェースをコピーします。
- リモートインターフェースアクセス用クラスの作成 - サーバーレジストリに接続し、Naming.lookupメソッドでサービスを検索してスタブを取得します。
- スタブ経由でのリモートメソッド呼び出し
実装サンプル
サーバー側コード
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface GreetingService extends Remote {
public String greetUser() throws RemoteException;
}
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class GreetingServiceImpl extends UnicastRemoteObject implements GreetingService {
protected GreetingServiceImpl() throws RemoteException {
super();
}
@Override
public String greetUser() throws RemoteException {
return "ようこそ、リモートサービスへ!";
}
public static void main(String[] args) {
try {
GreetingService remoteService = new GreetingServiceImpl();
Naming.rebind("GreetingService", remoteService);
System.out.println("サービス登録完了");
} catch (Exception e) {
e.printStackTrace();
}
}
}
クライアント側コード
import java.rmi.Naming;
public class ClientApplication {
public static void main(String[] args) {
try {
GreetingService serviceRef = (GreetingService)
Naming.lookup("rmi://localhost/GreetingService");
String response = serviceRef.greetUser();
System.out.println(response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
スタブの動作原理
リモートインターフェースがスタブを直接呼び出せる理由は、スタブが実際にはリモートインターフェースの実装クラスとして動的に生成されるためです。以前はrmicツールでスタブを静的に生成していましたが、現在は動的生成が主流となっています。スタブはクライアント側のプロキシとして機能し、ローカルオブジェクトのように操作できるようにします。