このページでは、カスタムの 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 CompressionClientSocketFactory
implements 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 CompressionServerSocketFactory
implements 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 MultiServerSocketFactory
implements 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 now
permission 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 |
![]() |