Java 

    カスタム RMI ソケットファクトリの使用

ドキュメンテーションコメント
 

このチュートリアルでは、カスタム RMI ソケットファクトリを実装して使用する方法を説明します。カスタム RMI ソケットファクトリは次のような場合に役立ちます。(1) RMI クライアントとサーバで、データの暗号化や圧縮を行うソケットを使う必要がある場合、または (2) アプリケーションで、リモート接続ごとに異なるソケットタイプを必要とする場合、あるいは (1) と (2) の両方の場合です。

JavaTM 2 SDK v1.2 より前のリリースでは、RMI トランスポートによって作成されたすべての接続でグローバルに使用されるカスタム java.rmi.server.RMISocketFactory サブクラスを作成してインストールすることができました。しかし、オブジェクトごとに異なる RMI ソケットファクトリを関連付けることはできませんでした。たとえば JDKTM v1.1.x では、RMI ソケットファクトリで 1 つのオブジェクトに SSL ソケットを作り、同一の仮想マシン内にある別のオブジェクトに対して TCP 上で直接 Java Remote Method Protocol (JRMP) を使用することはできませんでした。また JDK 1.2 より前は、自分のカスタムソケットのプロトコルだけを使用する rmiregistry のインスタンスを生成する必要がありました。

Java 2 SDK v1.2 リリースでは、RMI アプリケーションで、オブジェクト単位でカスタム RMI ソケットファクトリを使用し、クライアント側ソケットファクトリをダウンロードし、デフォルトの rmiregistry の使用を継続することができます。

このチュートリアルは次の 3 つの部分に分かれています。

このチュートリアルで使われているソースコードは、次のファイル形式から選ぶことができます。

RMI クライアントとサーバ間の安全な通信に関心を持つ人は多いでしょう。詳細は、「RMI での SSL の使用」 を参照してください。


カスタム RMI ソケットファクトリの実装

カスタム RMI ソケットファクトリは、次の 3 つのステップで実装します。
  1. カスタム ServerSocketSocket を実装する
  2. カスタム RMIClientSocketFactory を実装する
  3. カスタム RMIServerSocketFactory を実装する

ステップ 1:
カスタム ServerSocketSocket を実装する

使用するソケットのタイプは、アプリケーションの仕様によって決まります。サーバが機密データを送信または受信する場合は、データを暗号化するソケットを選びます。

この例のカスタム RMI ソケットファクトリは、単純な XOR 暗号化を実行するソケットを生成します。このタイプの暗号化は、回線上のパケットを偶然に覗き見られることがないようにデータを保護しますが、暗号解読の知識のある者であれば復号化は容易です。

カスタム XOR ソケットの実装には、次のソースが含まれます。XOR ソケットは、特殊な入力と出力のストリーム実装を使用して、ソケットに対して読み書きされるデータの xor 処理を行います。

ステップ 2:
カスタム RMIClientSocketFactory を実装する

クライアント側 RMI ソケットファクトリ XorClientSocketFactory は、java.rmi.server.RMIClientSocketFactory インタフェースを実装します。クライアントソケットファクトリは、createSocket メソッドを実装して、適切なクライアントソケットインスタンス XorSocket を返す必要があります。

クライアントソケットファクトリは、java.io.Serializable を実装して、インスタンスがクライアントに直列化されるようにする必要があります。また、equals メソッドと hashCode メソッドを実装して、同等のファクトリを使用するリモートオブジェクトへのソケットファクトリの接続を RMI 実装が再利用できるようにすることも重要です。

package examples.rmisocfac;

import java.io.*;
import java.net.*;
import java.rmi.server.*;

public class XorClientSocketFactory
    implements RMIClientSocketFactory, Serializable {

    private byte pattern;

    public XorClientSocketFactory(byte pattern) {
	this.pattern = pattern;
    }

    public Socket createSocket(String host, int port)
	throws IOException
    {
	return new XorSocket(host, port, pattern);
    }

    public int hashCode() {
	return (int) pattern;
    }

    public boolean equals(Object obj) {
	return (getClass() == obj.getClass() &&
		pattern == ((XorClientSocketFactory) obj).pattern);
    }
}

ステップ 3:
カスタム RMIServerSocketFactory を実装する

サーバ側 RMI ソケットファクトリ XorServerSocketFactory は、java.rmi.server.RMIServerSocketFactory インタフェースを実装します。サーバソケットファクトリは、createServerSocket メソッドを実装して、適切なサーバソケットインスタンス XorServerSocket を返す必要があります。

サーバソケットファクトリのインスタンスはクライアントに直列化されないので、サーバソケットファクトリは Serializable インタフェースを実装する必要はありません。サーバソケットファクトリは、equals メソッドと hashcode メソッドを実装して、同等のファクトリに関するソケットファクトリの受け入れ接続を RMI 実装が再利用できるようにする必要があります。

package examples.rmisocfac;

import java.io.*;
import java.net.*;
import java.rmi.server.*;

public class XorServerSocketFactory
    implements RMIServerSocketFactory {

    private byte pattern;

    public XorServerSocketFactory(byte pattern) {
	this.pattern = pattern;
    }

    public ServerSocket createServerSocket(int port)
	throws IOException
    {
	return new XorServerSocket(port, pattern);
    }

    public int hashCode() {
	return (int) pattern;
    }

    public boolean equals(Object obj) {
	return (getClass() == obj.getClass() &&
		pattern == ((XorServerSocketFactory) obj).pattern);
    }

}

アプリケーションでのカスタムソケットファクトリの使用

リモートオブジェクトでカスタム RMI ソケットファクトリを使用するのに必要な追加のステップは、2 つだけです。
  1. カスタムの RMIClientSocketFactory および RMIServerSocketFactory を使用するリモートオブジェクトを作成してエクスポートするサーバアプリケーションを作成する。リモートオブジェクトのスタブへの参照を RMI レジストリに保存して、クライアントがそれを検索できるようにする

  2. リモートオブジェクトのスタブを検索してリモートメソッドを呼び出すクライアントアプリケーションを作成する。カスタムソケットファクトリは、クライアントアプリケーション内で参照される必要はない。クライアントがリモートオブジェクトのスタブを検索すると、クライアント側 RMI ソケットファクトリがクライアントにダウンロードされる

ステップ 1:
サーバアプリケーションを作成する

リモートオブジェクトとの通信でカスタムソケットを使用する必要がある場合は、リモートオブジェクトをエクスポートするときに使用するカスタムソケットファクトリを RMI ランタイムに知らせる必要があります。カスタムソケットファクトリを指定するオブジェクトがアプリケーションからエクスポートされると、RMI ランタイムは、対応するカスタム RMIServerSocketFactory を使用して、そのリモートオブジェクトへの着信呼び出しを受け入れるためのサーバソケットを作成します。また、RMI ランタイムは、対応するカスタム RMIClientSocketFactory を参照するスタブも作成します。このクライアントソケットファクトリは、そのスタブを使用するリモートオブジェクトへのリモート呼び出しを起動するときに接続を作成するために使用されます。

この例は、「RMI 入門」チュートリアルの例に似ていますが、RMI 実装が使用するデフォルトのソケットではなく、カスタムソケットファクトリを使用します。

このアプリケーションは、次の Hello リモートインタフェースを使用します。

package examples.rmisocfac;

public interface Hello extends java.rmi.Remote {
    String sayHello() throws java.rmi.RemoteException;
}
このサーバアプリケーションは、Hello リモートインタフェースを実装するリモートオブジェクトを作成してエクスポートし、カスタムソケットファクトリを引数にとる java.rmi.server.UnicastRemoteObject.exportObject メソッドを使ってソケットファクトリを使用します。次に、ローカルレジストリを作成し、そのレジストリ内で、リモートオブジェクトのスタブへの参照を「Hello」という名前でバインドします。
package examples.rmisocfac;

import java.io.*;
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;

public class HelloImpl implements Hello {

    public HelloImpl() {}

    public String sayHello() {
        return "Hello World!";
    }

    public static void main(String args[]) {

	if (System.getSecurityManager() == null) {
	    System.setSecurityManager(new SecurityManager());
	}

        byte pattern = (byte) 0xAC;
	try {
	    /*
	     * Create remote object and export it to use
	     * custom socket factories.
	     */
	    HelloImpl obj = new HelloImpl();
	    RMIClientSocketFactory csf = new XorClientSocketFactory(pattern);
	    RMIServerSocketFactory ssf = new XorServerSocketFactory(pattern);
	    Hello stub =
		(Hello) UnicastRemoteObject.exportObject(obj, 0, csf, ssf);

	    /*
	     * Create a registry and bind stub in registry.
	     *
	    LocateRegistry.createRegistry(2002);
	    Registry registry = LocateRegistry.getRegistry(2002);
	    registry.rebind("Hello", stub);
	    System.out.println("HelloImpl bound in registry");

	} catch (Exception e) {
	    System.out.println("HelloImpl exception: " + e.getMessage());
	    e.printStackTrace();
	}
    }
}

ステップ 2:
クライアントアプリケーションを作成する

クライアントアプリケーションは、サーバアプリケーションが使用するレジストリへの参照を取得します。次に、リモートオブジェクトのスタブを検索して、リモートメソッド sayHello を呼び出します。

package examples.rmisocfac;

import java.rmi.*;
import java.rmi.registry.*;

public class HelloClient {

    public static void main(String args[]) {

	if (System.getSecurityManager() == null) {
	    System.setSecurityManager(new SecurityManager());
	}

        try {
	    Registry registry = LocateRegistry.getRegistry(2002);
            Hello obj = (Hello) registry.lookup("Hello");
            String message = obj.sayHello();
            System.out.println(message);

        } catch (Exception e) {
	    System.out.println("HelloClient exception: " +
                               e.getMessage());
            e.printStackTrace();
        }
    }

}


アプリケーションのコンパイルと実行

アプリケーションのコンパイルおよび実行は、次の 4 つのステップで行います。

  1. リモートインタフェース、クライアント、およびサーバの各クラスをコンパイルする
  2. 実装クラス上で rmic を実行する
  3. サーバを起動する
  4. クライアントを実行する

ステップ 1:
リモートインタフェース、クライアント、およびサーバの各クラスをコンパイルする

javac -d . Hello.java
javac -d . HelloClient.java
javac -d . HelloImpl.java
javac -d . XorClientSocketFactory.java
javac -d . XorInputStream.java
javac -d . XorOutputStream.java
javac -d . XorServerSocket.java
javac -d . XorServerSocketFactory.java
javac -d . XorSocket.java

ステップ 2:
実装クラス上で rmic を実行する

rmic -d . examples.rmisocfac.HelloImpl

ステップ 3:
サーバを起動する

java -Djava.security.policy=policy examples.rmisocfac.HelloImpl

サーバ側の出力は、次のようになります。

      HelloImpl bound in registry

ステップ 4:
クライアントを実行する

別のウィンドウでクライアントアプリケーションを起動し、アプリケーションのクラスが次のクラスパス内にあることを確認します。

java -Djava.security.policy=policy examples.rmisocfac.HelloClient

クライアント側の出力は次のようになります。

      Hello World!  

注: このサーバアプリケーションとクライアントアプリケーションはどちらも、セキュリティポリシーファイルを使用して、ローカルのクラスパス (カレントディレクトリ) 内のファイルへのアクセス権のみを付与します。サーバアプリケーションは接続を受け入れる権限を必要とし、サーバアプリケーションとクライアントアプリケーションの両方が、接続を作成する権限を必要とします。指定されたコードベース URL (カレントディレクトリからの相対位置を表す「file:」URL) にアクセス権 java.net.SocketPermission が付与されます。このアクセス権では、特権を持たないポート (1024 以上のポート) 上で、任意のホストからの接続を受け入れること、および任意のホストへの接続を行うことが許可されます。

grant codeBase "file:." {

    permission java.net.SocketPermission "*:1024-", "connect,accept";
};


Copyright © 2001 Sun Microsystems, Inc. All Rights Reserved. 
コメントの送付先: rmi-comments@java.sun.com 
Sun