[Java How To Programming] [Home on 246net] [Home on Alles net]
Powered by SmartDoc

スタブ不要なRMI

TAKAHASHI,Toru
torutk@alles.or.jp

目次

スタブを生成せずにRMIを使用する

初期のJavaにおけるRMIを利用した分散アプリケーションでは、通信処理を行うためのスタブ・スケルトンをrmicコマンドを用いて事前に生成しておく必要がありました。スタブはクライアント側に配置し、スケルトンはサーバ側に配置していました。RMIのバージョンが上がって(JRMP1.2)、スケルトンは生成する必要がなくなりましたが、スタブは必要でした。J2SE 1.5からは、スタブの生成も不要になり、実質的には通常のJavaプログラムを作る手順だけでRMIプログラムを作ることができるようになりました。

Helloプログラム

Helloサーバのソース

遠隔から呼び出すインタフェース(通称リモート・インタフェース)を定義します。java.rmi.Remoteを継承することと、メソッドは必ずjava.rmi.RemoteExceptionをスローするように定義します。また、メソッドの引数・戻り値に使用する型は、シリアライズ可能でなくてはなりません。

リモートインタフェース
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Hello extends Remote {
    String sayHello() throws RemoteException;
}

遠隔から呼び出されたメソッドの実装を定義します。実装方法には幾種類かがありますが、最もよく使われるものは、java.rmi.server.UnicastRemoteObjectクラスを継承する方法です。

リモート実装クラス
import java.rmi.server.UnicastRemoteObject;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
public class HelloImpl extends UnicastRemoteObject implements Hello {
    /**
     * 注記:スーパークラスのUnicastRemoteObjectのコンストラクタにて
     * RemoteExceptionがスローされるので、本コンストラクタにおいて
     * これをcatchするかthrowするかが必要である。本実装においては、
     * throwを選択している。
     */
    public HelloImpl() throws RemoteException {
    }
    
    public String sayHello() {
        return "Hello, RMI World!";
    }
    
    public static void main(String[] args) {
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new RMISecurityManager());
        }

        try {
            Hello hello = new HelloImpl();
            Naming.rebind("HelloObject", hello);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Helloクライアントのソース

リモートクライアント
import java.rmi.RMISecurityManager;
import java.rmi.Naming;

public class HelloClient {
    public static void main(String[] args) {
        Hello hello;
        try {
            System.setSecurityManager(new RMISecurityManager());

            hello = (Hello)Naming.lookup("rmi://localhost/HelloObject");

            System.out.println(hello.sayHello());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

RMIを使用する際のポリシーファイル

実験用なので、セキュリティを全て許可にしています。

ポリシーファイル
grant {
    permission java.security.AllPermission;
};

ディレクトリ構成

RMIプログラムを作成し、コンパイル・実行する際の注意点として、サーバ側とクライアント側を同じディレクトリやクラスパスで参照できる場所に置かないことがあります。これは、ローカルマシンでサーバとクライアントを実行するときは、実はクラスファイルをネットワークからではなくローカルディスクからクラスパスで参照してしまう問題を避けるためです。また、rmiregistryもクラスパス上にクラスファイルを見つけると、指定されたコードベースを無視してローカルのクラスパスを使用してしまいます。

そこで、クライアントとサーバ双方が参照するリモートインタフェースを共通のディレクトリに置き、それ以外のクライアント固有、サーバ固有のクラスは個別に置きます。

ディレクトリ構成
client HelloClient.java
ifc Hello.java
java.policy
server HelloImpl.java

コンパイル

リモートインタフェースのコンパイル

dev$ cd ifc
ifc$ javac Hello.java
ifc$ ls
Hello.class Hello.java java.policy
ifc$

サーバのコンパイル

dev$ cd server
server$ javac -classpath .:../ifc HelloImpl.java
server$ ls
HelloImpl.class HelloImpl.java
server$

クライアントのコンパイル

dev$ cd client
client$ javac -classpath .:../ifc HelloClient.java
client$ ls
HelloClient.class HelloClient.java
client$

実行

  1. rmiregistryの実行
  2. サーバの実行
  3. クライアントの実行

UNIX系OS(shell)上での実行

rmiregistryの実行
dev$ unset CLASSPATH
dev$ rmiregistry
サーバの実行
server$ java -cp .:../ifc \
    -Djava.security.policy=../ifc/java.policy \
    -Djava.rmi.server.codebase=file:///home/torutk/dev/ifc/ \
    HelloImpl
クライアントの実行
client$ java -cp .:../ifc \
    -Djava.security.policy=../ifc/java.policy HelloClient
Hello, RMI World!
client$

WindowsOS(コマンドプロンプト)上での実行

rmiregistryの実行
dev> set CLASSPATH=
dev> rmiregistry
サーバの実行
server> java -cp .;..\ifc \
    -Djava.security.policy=..\ifc\java.policy \
    -Djava.rmi.server.codebase=file:///C:/home/torutk/dev/ifc/ \
    HelloImpl
クライアントの実行
client> java -cp .;..\ifc \
    -Djava.security.policy=..\ifc\java.policy HelloClient
Hello, RMI World!
client>