RMIとは、2つの別のマシン上で動作するJavaプログラムの一方のオブジェクトのメソッドを、他方のプログラムから呼び出す機能を実現するための仕組みです。この記事ではRMIの使い方をサンプルを交えてご紹介します。
RMIとは、2つの別のマシン上で動作するJavaプログラムの一方のオブジェクトのメソッドを他方のプログラムから呼び出す機能を実現するための仕組みです。通信部分のコードを作成することなくクライアント・サーバ機能を実現することができることがメリットです。他のマシン上のオブジェクト(リモート・オブジェクト)に対して次のことが可能です。
リモートオブジェクトを別のリモートオブジェクトのメソッドに渡すようなことも可能です。
クライアントからアクセス可能にするオブジェクトのインタフェースを定義する。
public interface MyObject extends Remote {
public String getMessage() throws java.rmi.RemoteException;
}
Remoteオブジェクトのサブクラスであることと、メソッドはすべてRemoteExceptionを発生することを宣言するのがポイントです。
ここで定義したインタフェースに従ってリモートオブジェクトのクラスを作成する。
public class MyObjectImpl extends java.rmi.server.UnicastRemoteObject {
implements MyObject {
public String getMessage() throws java.rmi.RemoteException {
return "Hello world!";
}
public MyObjectImpl throws java.rmi.RemoteException {}
}
サーバプログラム(Server.java)を作成し、その中(たとえばmain()メソッド中)から次のような処理を実行する。
public class Server {
public static void main(String args[]) {
try {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new java.rmi.RMISecurityManager());
}
MyObjectImpl myobj = new MyObjectImpl();
java.rmi.Naming.rebind("//localhost/MyObject", myobj);
System.error.println("Request Object ready.");
}
catch (SecurityException e) {
System.error.println("Failed to regist request.");
}
catch (Exception e) {
System.error.println("Failed to regist request.");
System.error.println(e);
}
}
}
クライアントプログラムを作成し、その中からリモートオブジェクトの登録名を引数にしてNaming.lookup()メソッドを呼ぶ。
public class Client {
public static void main(String args[]) {
MyObject myobj = null;
try {
System.setSecurityManager(new java.rmi.RMISecurityManager());
String name = "rmi://localhost/MyObject";
myobj = (MyObject)java.rmi.Naming.lookup(name);
}
catch (Exception e) {
System.error.println(e);
}
try {
System.out.println(myobj.getMessage());
}
catch (Exception e) {
System.error.println(e);
}
}
}
lookup()
メソッドによって、リモートオブジェクトが変数myobj
に返されるので、後は通常どおりmyobj
のメソッドを利用して様々な処理を行えばよい。
通常通りソースコードをコンパイルし、classファイルを作成する。
javac *.java
リモートオブジェクトをコンパイルするには rmic コマンドを使います。 rmic コマンドの引数にはリモートオブジェクトのクラス名を指定します。
rmic MyObjectImpl
この結果、MyObjectImpl_Stub.class(スタブクラス)とMyObjectImpl_Skel.class(スケルトンクラス)という2つのクラスファイルが生成される。
スタブクラスとインタフェースクラス(MyObject.class)をWWWサーバー経由でアクセスできる場所にコピーする。
スタブクラスとスケルトンクラスはサーバ側にコピーします。
スタブクラスはリモートオブジェクトとまったく同じメソッドからできているクラスで、その中身は単にメソッド呼び出しをリモートオブジェクトに中継するだけの機能を果たします。
スケルトンクラスはスタブクラスから渡ってきた引数をストリームから取り出し、それをサーバ側のリモートオブジェクトに中継する役割を果たします。
クライアントクラス (Client.class) をクライアントマシンに移動します。
次のような3種類のパーミッションをポリシーに記述しなければなりません。
これらをすべて満たすポリシーを記述するのは難しいし、実際どのXXXPermissionを記述すればよいのかも明確にされていません。
Java 2 のポリシーファイル (java.policy) に対し、何でも許してしまうという記述のサンプルを以下に示します。
grant {
permission java.security.AllPermission;
}
サーバマシン上でレジストリサーバを起動する。Serverクラスを実行するマシンと同じマシンでなければならないことに注意。そのためには、まずカレントディレクトリからスタブクラスが参照できないことを確認する。
java MyObjectImpl_Stub
Exception in thread "main" java.lang.NoClassDefFoundError: MyObjectImpl_Stub
MyObjectImpl_Stubが参照できないことを確認したら、Java 2の場合はrmiregistryコマンドを起動する。
C:\> start rmiregistry
※Windows版の場合
Webサーバ経由でスタブクラスにアクセスできる場合:
java -D java.rmi.server.codebase=http://localhost//xxx/xxx/Server
Webサーバ経由でスタブクラスにアクセスできない場合:
java -Djava.rmi.server.codebase=file:/C:/xxx/xxx/Server
これにより、「MyObject」という名前でMyObjectオブジェクトがレジストリサーバ (rmiregistry) に登録される。
このとき「-D」オプションによってスタブクラスを置いたディレクトリのURLを指定しなければならない。URLとしては「http:」または「file:」が使えます。「-D」オプションの最後のスラッシュは必ずつけます。
$ java Client
Hello world!
RMIでは、サーバプログラムによって登録されたオブジェクトを複数のクライアントで共有することになります。したがって、例えば2つのクライアントが順番にリモートオブジェクトの変数を書き換える場合、2つ目のクライアントが実行された時点でその変数は1つ目のクライアントによってすでに書き変わっているということです。しかし、クライアントがアクセスするたびにリモートオブジェクトの新しいインスタンスを入手したい場合があります。こういう場合は「クライアント別のリモートオブジェクトをインスタンス化して返すリモートオブジェクト」を作ればよいのです。すなわち、次のようなリモートオブジェクトを作ります。
public class MyObject1Impl extends UnicastRemoteObject
implements MyObject1 {
public MyObject2 getMyObject2() throws RemoteException {
return new MyObject2Impl();
}
}
public class MyObject2Impl extends UnicastRemoteObject
implements MyObject2 {
}
これを次のようなクライアントコードでアクセスします。
System.setSecurityManager(new RMISecurityManager());
String name = "rmi://localhost/MyObject1";
MyObject1 obj1 = (MyObject1)Naming.lookup(name);
MyObject2 obj2 = obj1.getMyObject2();
Windows 2000の場合はrmiregistryとサーバプログラムをサービスとして登録しなければならない。その方法には主に2種類あります。
前者はサービス起動時に呼び出される関数を登録したり、Windowsのサービスコントロールマネージャにプロセスの状態を通知したりすることができる。