JDK 1.1でのjava.net
クラスのいくつかの改善によって、ソケット(Socket/ServerSocket)をfinalでない拡張可能なクラスにできるようになりました。基本的な目標は、アプリケーション・コードを書き直さずに、基底クラスSocketが使用されているところではどこでも拡張されたソケットを使用できるようにすることです。そこでたとえば、既存の電子メール・クライアントには、Socketのサブクラスが透過的に処理するピア認証または暗号化を行うSocketのサブクラスを渡すことが可能です。ほかの例としては、圧縮を透過的に使用するソケット、または複数のサーバーにトラフィックのミラーリングなどの機能を追加するソケットがあります(信頼できる順番付けられたマルチキャストの使用など)。
このドキュメントには次のセクションがあります。
JDK 1.0では、ある非常に特別な方法でユーザーがソケットの機能を拡張できるようにしました。ユーザーはjava.net.SocketImpl
をサブクラス化し、必要に応じてこのようなクラスを返す拡張されたjava.net.SocketImplFactory()を提供できます。
SocketImpl/SocketImplFactoryスキームは、異なるトランスポート・メカニズムを使用する環境間で移植性のあるJavaアプレットおよびアプリケーションを作成するのに有用であり、そのために設計されました。java.net.Socketを使用するクライアント・アプリケーションは、ネットワーク接続が特定のことを実行する必要のある環境においてだけでなく、一般的なケース(Java RuntimeがPlainSocketImplを使用する)でも動作することができます。たとえば、その環境で適切な種類のSocketImplがJava Runtimeに対して設定されていると仮定すると、SOCKSのようなプロキシ・プロトコルを経由してインターネットに接続する必要があるファイアウォールの背後でも、Javaのプログラムは動作できなければなりません。
SocketImplはJavaのアプレットにプロキシ・サポートを透過的に提供するようなものにとって有用ですが、このユーティリティは、ネットワーク・トランスポートの追加機能を提供する方法、あるいはTCPの上部にほかのプロトコルを階層化する方法としては限定されています。さらに、Java Runtimeに対しては単一型のSocketImplをインストールすることだけが可能なため、大規模なアプリケーションを制限することになります。ServerSocketおよびSocketを拡張可能にする方法は明確であり、直感的にわかりやすいものです。
SocketImplメカニズムは、Socketのサブクラスが提供する機能に直交するように設計されています。たとえば、そのストリーム上で圧縮を実行できるSocketのサブクラスは、依然システム・デフォルトのSocketImplを使用し、ある種のファイアウォールの背後でプロキシ・サポートを得たいと希望する可能性があります。システム・デフォルトのSocketImplは、アプリケーション側からの配慮を必要としないトランスポート層の拡張として考えることができます。そして、Socket/ServerSocketのサブクラスはアプリケーション層で豊富な機能を提供します。
JDK 1.1では、SocketおよびServerSocketは非finalになり、これはきわめて簡単な変更です。主にセキュリティ上の理由で、サブクラスは、基底クラスの配下のSocketImplに直接アクセスできないことに注意してください。しかし、それ以外ではSocket/ServerSocketのサブクラスは、そのスーパー・クラスのメソッドを継承しオーバーライドできます。
JDKは次のように変更されました。
Socket
およびServerSocket
クラスからfinal
修飾子を削除。final
修飾子を、セキュリティ・マネージャの呼出しを迂回しないようにするために必要なメソッドだけに再接続。protected final void implAccept(Socket client)
とともに定義。Socket
コンストラクタを公開。これは、ServerSocketサブクラスがaccept()から正しいSocketサブクラスを返すためにも必要(FooServerSocket.accept()はFooSocketを返すなど)次の一般形式のSocketのpublicコンストラクタは、スーパー・クラスを初期化するために使用できます。
Socket(String host, int port) { ... }
ただし、これらはまたシステム・デフォルトのSocketImplを生成し、それを指定したホスト、ポートに接続します。Socketは、Socketに接続せずにスーパー・クラスを初期化するために2つのprotectedコンストラクタを持ちます。
protected Socket() { /* install system-default SocketImpl */ ... } protected Socket(SocketImpl impl) { this.impl = impl; }
最初のコンストラクタはシステム・デフォルトのSocketImplをインストールします(ファクトリまたはPlainSocketImplのどちらかから)。2番目のコンストラクタは、Socketのサブクラスが必要に応じて自分のimplをインストールできるようにします。SocketサブクラスがデフォルトのSocketImplを必要としない場合、2番目のコンストラクタを使ってそれにnullを渡すことは完全に有効です。ただし、このケースでは、サブクラスはすべて配下のSocketImplに依存しているため、すべての基底クラス・メソッドをオーバーライドする必要があります。
ServerSocketのサブクラスはまた、これらに公開されるprotectedコンストラクタを持ち、このコンストラクタは基底クラス内でデフォルトのSocketImplを生成します。しかし、そうでない場合、これを初期化します。たとえば、impl.create()、impl.bind()、impl.listen()を呼び出しません。ServerSocketのpublicコンストラクタは、配下のSocketImplを初期化します。
protected ServerSocket() { /* install system-default SocketImpl */ ... }
その他にServerSocketの拡張に関して説明すべきことは、Socket/ServerSocketの配下のSocketImplがサブクラスにアクセス可能でない場合にaccept()をオーバーライドする方法のみです。基底クラスはこれを行うことができるため、ServerSocketはサブクラスの代わりに配下のSocketImplに対して必要な呼出しを行うための次のメソッドを持ちます。
public class ServerSocket { ... protected final void implAccept(Socket s) throws IOException { ... // on return from this call s will be connected to a client } ...
SocketImplを使用しないSocket/ServerSocketのサブクラスは、このメソッドを使用する必要はありません。この機能方法について、次にSSLコードの例を示します。
class SSLServerSocket extends ServerSocket {
...
public Socket accept () throws IOException
{
SSLSocket s = new SSLSocket (certChain, privateKey);
// create an unconnected client SSLSocket, that we'll
// return from accept
implAccept (s);
s.handshake ();
return s;
}
...
}
class SSLSocket extends java.net.Socket {
...
public SSLSocket(CertChain c, PrivateKey k) {
super();
...
}
...
}