このページでは、カスタムの RMI ソケットファクトリを作成してインストールする方法を説明します。カスタム RMI ソケットファクトリは次のような場合に役立ちます。 (1) RMI クライアントとサーバの通信をソケットを通じて行い、ソケットでデータの暗号化や圧縮を行う場合、または (2) 接続ごとに異なるソケットを使用する場合です。
自分専用の RMI ソケットファクトリをインストールすることにより、RMI トランスポート層が IP 上で TCP 以外のカスタムトランスポートプロトコルを使用できるようになります。 デフォルトでは、RMI は java.net.Socket により提供される TCP を使用します。
JavaTM 2 SDK, v1.2 より前のリリースでも、カスタムの java.rmi.RMISocketFactory サブクラスを作成して、java.net.Socket 以外のタイプのソケットを作成して RMI のトランスポート層で使用することができました。しかし、インストールされた RMI ソケットファクトリではオブジェクトごとに異なるタイプのソケットを作成することはできませんでした。たとえば JDK 1.1 では、RMI ソケットファクトリで 1 つのオブジェクトに SSL ソケットを作り、同一の Java Virtual Machine (JVM) 内の別のオブジェクトに対して TCP 上で直接 Java Remote Method Protocol (JRMP) を使用することはできませんでした。また JDK 1.2 より前は、自分のカスタムソケットのプロトコルだけを使用する rmiregistry のインスタンスを生成する必要がありました。
Java SDK, v1.2 ベータ 3 では、RMI クライアントがカスタムの RMI ソケットファクトリを使用できるようになりましたが、このソケットファクトリはダウンロードすることができなかったので、クライアントがこのソケットファクトリクラスをローカルで検索できる必要がありました。
Java 2 SDK, v1.2 のリリースから、必要な時にオブジェクトごとに異なる種類のソケット接続を生成するカスタム RMI ソケットファクトリを作成し、クライアント側のソケットファクトリをダウンロードして、デフォルトの rmiregistry をそのまま使うことができます。
このチュートリアルの構成は次のとおりです。
RMI クライアントとサーバ間の安全な通信に関心を持つ人は多いでしょう。RMI と SSL の関係については「RMI と SSL について」を参照してください。
RMIClientSocketFactory を実装するRMIClientSocketFactory の createSocket メソッドを実装するRMIServerSocketFactory を実装するRMIServerSocketFactory の createServerSocket メソッドを実装するステップ 1:
生成するソケットタイプは、アプリケーションの仕様により決まります。アプリケーションに適したソケットタイプを選択します。サーバが大量の機密データを扱う場合は、データを暗号化するソケットを選びます。サーバがビデオデータを扱う場合は、データを圧縮するソケットが必要です。
生成するソケットタイプを決定するこの例の RMI ソケットファクトリは、データを圧縮するソケットを生成します。「カスタムソケットタイプの作成」のページで
examples.rmisocfac.CompressionSocketソケットを作成します。ステップ 2:
クライアント側の RMI ソケットファクトリの実装は
クライアント側のソケットファクトリを記述してRMIClientSocketFactoryを実装するRMIClientSocketFactoryインタフェースの実装により開始します。この例のカスタムソケットファクトリは、CompressionClientSocketFactoryという名前です。次のコード例は、
CompressionClientSocketFactoryクラスのコードと、その次のステップ (createSocketメソッドをオーバーライドする) のコードです。次のステップについては、コード例の次に説明します。
package examples.rmisocfac;
import java.io.*;import java.net.*;import java.rmi.server.*;
public class CompressionClientSocketFactoryimplements RMIClientSocketFactory, Serializable {
public Socket createSocket(String host, int port)throws IOException{CompressionSocket socket =new CompressionSocket(host, port);return socket;}
}
ステップ 3:
RMI ソケットファクトリの機能は RMI ランタイムにソケットを提供することなので、RMIClientSocketFactoryのcreateSocketメソッドを実装するCompressionClientSocketFactoryには、正しいタイプのソケット (CompressionSocket) を作成して返すRMIClientSocketFactory createSocketメソッドを実装する必要があります。上のコード例で、CompressionSocketが作成されて返されていることに注目してください。ステップ 4:
サーバ側の RMI ソケットファクトリの実装は
サーバ側のソケットファクトリを記述してRMIServerSocketFactoryを実装するRMIServerSocketFactoryインタフェースの実装により開始します。この例のカスタムソケットファクトリは、CompressionServerSocketFactoryという名前です。次のコード例は、
CompressionServerSocketFactoryクラスのコードと、その次のステップ (createServerSocketの実装) のコードです。次のステップについては、コード例の次に説明します。
package examples.rmisocfac;
import java.io.*;import java.net.*;import java.rmi.server.*;public class CompressionServerSocketFactoryimplements RMIServerSocketFactory, Serializable {
public ServerSocket createServerSocket(int port)throws IOException{CompressionServerSocket server = new CompressionServerSocket(port);return server;}}
ステップ 5:
RMI ソケットファクトリにRMIServerSocketFactoryのcreateServerSocketメソッドを実装するcreateServerSocketを実装する方法はcreateSocketを実装する方法とほとんど同じですが、createServerSocketではソケットタイプCompressionServerSocketを作成して返す必要がある点が異なります。これで、RMI ソケットファクトリの作成例を 1 つ学びました。次に、複数タイプのソケットを生成できるソケットファクトリの作成の例を学びます。
この例では、カスタムの RMIClientSocketFactory クラスは複数のソケットタイプを扱うので、MultiClientSocketFactory という名前にします。 同様に、カスタムの RMIServerSocketFactory クラスの名前は MultiServerSocketFactory です。各ソケットファクトリには、このオブジェクトインスタンスにどのプロトコルをサポートするかを指定するコンストラクタがあります。
すでに説明した例と同じ手順でカスタムソケットファクトリの作成を開始します。 まず、生成するソケットタイプを決定します。
ステップ 1:
このカスタム RMI ソケットファクトリは、
生成するソケットタイプを決定するXorSocket、CompressionSocket、およびデフォルトのjava.net.Socketの 3 つのタイプのソケットを生成します。
XorSocketタイプのソケットの実装のソースコードを見るには、ここをクリックしてください。ステップ 1 はこれで終わりです。次のコード例は、ステップ 2 からステップ 5 のコードです。コード例のあとで、それぞれのステップについて説明します。
package examples.rmisocfac; import java.io.*; import java.net.*; import java.rmi.server.*;public class MultiClientSocketFactory implements RMIClientSocketFactory, Serializable { /* * Get the default RMISocketFactory */ private static RMISocketFactory defaultFactory = RMISocketFactory.getDefaultSocketFactory(); private String protocol; private byte[] data; public MultiClientSocketFactory(String protocol, byte[] data) { this.protocol = protocol; this.data = data; } /* * Override createSocket to call the default * RMIClientSocketFactory's createSocket method.This * way, you'll get a TCP connection if you don't * specify compression or xor */ public Socket createSocket(String host, int port) throws IOException { if (protocol.equals("compression")) { return new CompressionSocket(host, port); } else if (protocol.equals("xor")) { if (data == null || data.length != 1) throw new IOException("invalid argument for XOR protocol"); return new XorSocket(host, port, data[0]); } return defaultFactory.createSocket(host, port); } }ステップ 2:
上のコード例で、RMIClientSocketFactoryを実装するMultiClientSocketFactoryクラスはRMIClientSocketFactoryを実装します。
ステップ 3:
この例で作成するソケットファクトリはデフォルトタイプに加えて 2 つの異なるタイプのソケットを生成するので、RMIClientSocketFactoryのcreateSocketメソッドを実装するcreateSocketメソッドをオーバーライドする必要があります。MultiClientSocketFactoryコンストラクタのprotocolフィールドが「xor」の場合は、XorSocketが作成されて返されます。protocolフィールドが「compression」の場合は、CompressionSocketが作成されて返されます。
ステップ 4:
サーバ側ソケットファクトリを記述してRMIServerSocketFactoryを実装するpackage examples.rmisocfac;
import java.io.*;import java.net.*;import java.rmi.server.*;
public class MultiServerSocketFactoryimplements RMIServerSocketFactory, Serializable{/** Get the default RMISocketFactory*/private static RMISocketFactory defaultFactory =RMISocketFactory.getDefaultSocketFactory();
private String protocol;private byte[] data;
public MultiServerSocketFactory(String protocol, byte[] data) {this.protocol = protocol;this.data = data;}
/** Override createServerSocket to call the default* RMIServerSocketFactory's createServerSocket method, if* an invalid protocol is specified.*/public ServerSocket createServerSocket(int port)throws IOException{if (protocol.equals("compression")) {return new CompressionServerSocket(port);
} else if (protocol.equals("xor")) {if (data == null || data.length != 1)throw new IOException("invalid argument for XOR protocol");return new XorServerSocket(port, data[0]);
}
return defaultFactory.createServerSocket(port);}}ステップ 5:
RMIServerSocketFactoryのcreateServerSocketメソッドを実装するcreateServerSocketをオーバーライドする方法は、createSocketのオーバーライドとほとんど同じです。ステップ 3 と同様に、RMIServerSocketFactoryのcreateServerSocketメソッドで作成されて返されるソケットのタイプは、MultiClientSocketFactoryコンストラクタのprotocolフィールドによって決まります。
RMIClientSocketFactory パラメータと RMIServerSocketFactory パラメータを受け取る UnicastRemoteObject (または Activatable) コンストラクタを呼び出すコンストラクタを記述するjava.security.policy ファイルを記述するjava.security.policy ファイルを使ってアクセス権を適切に指定する方法については、次のドキュメントを参照してください。
「デフォルトの Policy の実装とポリシーファイルの構文」
「Java 2 SDK におけるアクセス権」
次は、ステップ 1:
カスタム RMI ソケットファクトリを作成する場合は、使用するソケットタイプを RMI ランタイムに通知する手段が必要です。サーバがRMIClientSocketFactoryとRMIServerSocketFactoryをパラメータにとるUnicastRemoteObjectコンストラクタを呼び出す、リモートオブジェクトコンストラクタを記述するUnicastRemoteObjectを継承すると仮定すると、この通知は、次のようなUnicastRemoteObjectコンストラクタを呼び出すリモートオブジェクトコンストラクタを作成することにより実現できます。
protected UnicastRemoteObject(int port, RMIClientSocketFactory csf,RMIServerSocketFactory ssf)次のコード例は、オリジナルの RMI 版「Hello World」を
XorSocketタイプのソケットに適合するように修正したHelloImplコンストラクタです。
public HelloImpl(String protocol, byte [] pattern)throws RemoteException{super(0, new MultiClientSocketFactory(protocol, pattern),new MultiServerSocketFactory(protocol, pattern));}
ここで、
UnicastRemoteObjectコンストラクタに注目してください。
protected UnicastRemoteObject(int port, RMIClientSocketFactory csf,RMIServerSocketFactory ssf)
HelloImplコンストラクタから呼び出されています。カスタムソケットファクトリが設定されたあとは、RMI クライアント/サーバアプリケーションで、目的のタイプのソケットが使用されます。ステップ 2:
この例で使用するポリシーファイルは安全ではないので、製作環境では使用しないでください。
プログラムに対してソケットの作成を許可するjava.security.policyファイルを記述するこの例で使用するポリシーファイルは次のようになります。
grant {// Allow everything for nowpermission java.security.AllPermission;};
MultiClientSocketFactory と MultiServerSocketFactory を使い、XorSocket タイプのソケットを使って通信する「Hello World」プログラムです。
この例は、オリジナルの RMI チュートリアルの「Hello World」の例を修正したものです。もっとも大きな違いは、この例のクライアントはアプレットではないということです。またこの例では、クライアントクラスの名前は HelloClient です。さらに、すべてのクラスが examples.rmisocfac パッケージ内にある点が異なります。
この例では、クライアント、サーバ、レジストリはすべて同一のマシンで実行されることにします。 この点は重要です。
以下は、ファイル Hello.java の Hello インタフェースです。パッケージ名以外は、このインタフェースはオリジナルの Hello.java から変更されていません。
package examples.rmisocfac;
public interface Hello extends java.rmi.Remote {
String sayHello() throws java.rmi.RemoteException;
}
HelloClient.java の修正されたバージョンを見てみます。main の最初に RMISecurityManager が組み込まれていることに注目してください。それ以外は、HelloClient クラスはオリジナルと同じです。
package examples.rmisocfac;
import java.rmi.*;
public class HelloClient {
private static String message = "";
public static void main(String args[]) {
//Create and install a security manager
if (System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
try {
Hello obj = (Hello) Naming.lookup("/HelloServer");
message = obj.sayHello();
System.out.println(message);
} catch (Exception e) {
System.out.println("HelloClient exception: " +
e.getMessage());
e.printStackTrace();
}
}
}
HelloImpl.java ファイルの HelloImpl のソースコードを修正したものです。パッケージ名の変更のほか、クライアントとサーバの間の RMI 呼び出しにソケットタイプ「xor」を使用できるように、2 つの修正が加えられています。コンストラクタが、クライアントとサーバのソケットファクトリをパラメータにとる UnicastRemoteObject コンストラクタを呼び出すように変更されていることに注目してください。
package examples.rmisocfac;
import java.io.*; import java.rmi.*; import java.rmi.server.*;
public class HelloImpl extends UnicastRemoteObject implements Hello { /* * Constructor calls constructor of superclass with * client and server socket factory parameters. */ public HelloImpl(String protocol, byte [] pattern) throws RemoteException { super(0, new MultiClientSocketFactory(protocol, pattern), new MultiServerSocketFactory(protocol, pattern)); }
/* * Remote method returns String "Hello World!" * when invoked. */ public String sayHello() throws RemoteException { return "Hello World!"; } public static void main(String args[]) {
//Create and install a security manager if (System.getSecurityManager() == null) System.setSecurityManager(new RMISecurityManager());
byte [] aPattern = { (byte)1011 }; try { HelloImpl obj = new HelloImpl("xor", aPattern); Naming.rebind("/HelloServer", obj); System.out.println("HelloServer bound in registry"); } catch (Exception e) { System.out.println("HelloImpl err:" + e.getMessage()); e.printStackTrace(); } } }
上記の「Hello World」プログラムのコンパイル方法および実行方法については、ここをクリックしてください。
* この Web サイトで使用されている用語「Java Virtual Machine」または「JVM」は、Java プラットフォーム用の仮想マシンを表します。
| Copyright © 1999 Sun Microsystems, Inc. All Rights Reserved. コメントの送付先: rmi-comments@java.sun.com |