カスタム 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 の使用」 を参照してください。このチュートリアルで使われているソースコードは、次のファイル形式から選ぶことができます。
ServerSocket
と Socket
を実装するRMIClientSocketFactory
を実装するRMIServerSocketFactory
を実装するステップ 1:
使用するソケットのタイプは、アプリケーションの仕様によって決まります。サーバが機密データを送信または受信する場合は、データを暗号化するソケットを選びます。
カスタムServerSocket
とSocket
を実装するこの例のカスタム RMI ソケットファクトリは、単純な XOR 暗号化を実行するソケットを生成します。このタイプの暗号化は、回線上のパケットを偶然に覗き見られることがないようにデータを保護しますが、暗号解読の知識のある者であれば復号化は容易です。
カスタム XOR ソケットの実装には、次のソースが含まれます。XOR ソケットは、特殊な入力と出力のストリーム実装を使用して、ソケットに対して読み書きされるデータの xor 処理を行います。
ステップ 2:
クライアント側 RMI ソケットファクトリ
カスタムRMIClientSocketFactory
を実装する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:
サーバ側 RMI ソケットファクトリ
カスタムRMIServerSocketFactory
を実装する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); } }
RMIClientSocketFactory
および RMIServerSocketFactory
を使用するリモートオブジェクトを作成してエクスポートするサーバアプリケーションを作成する。リモートオブジェクトのスタブへの参照を 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:
リモートインタフェース、クライアント、およびサーバの各クラスをコンパイルする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.javarmic -d . examples.rmisocfac.HelloImpljava -Djava.security.policy=policy examples.rmisocfac.HelloImplサーバ側の出力は、次のようになります。
HelloImpl bound in registry別のウィンドウでクライアントアプリケーションを起動し、アプリケーションのクラスが次のクラスパス内にあることを確認します。
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 |