これらのクラスのすべての新しいインスタンスにフックしてスパイすることができましjava.net.Socket
たjava.net.ServerSocket
。完全なコードは、ソース リポジトリで確認できます。アプローチの概要は次のとおりです。
Socket または ServerSocket がインスタンス化されると、そのコンストラクターで最初に行われるのはsetImpl()
、ソケット機能を実際に実装するオブジェクトをインスタンス化する呼び出しです。デフォルトの実装は のインスタンスですが、 とを介してjava.net.SocksSocketImpl
カスタムを設定することでオーバーライドできます。java.net.SocketImplFactory
java.net.Socket#setSocketImplFactory
java.net.ServerSocket#setSocketFactory
これは、パッケージ プライベートであるすべての実装によって少し複雑になりますjava.net.SocketImpl
が、それほど難しくないリフレクションが少しあります。
private static SocketImpl newSocketImpl() {
try {
Class<?> defaultSocketImpl = Class.forName("java.net.SocksSocketImpl");
Constructor<?> constructor = defaultSocketImpl.getDeclaredConstructor();
constructor.setAccessible(true);
return (SocketImpl) constructor.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
すべてのソケットを作成時にスパイするための SocketImplFactory 実装は、次のようになります。
final List<SocketImpl> allSockets = Collections.synchronizedList(new ArrayList<SocketImpl>());
ServerSocket.setSocketFactory(new SocketImplFactory() {
public SocketImpl createSocketImpl() {
SocketImpl socket = newSocketImpl();
allSockets.add(socket);
return socket;
}
});
setSocketFactory/setSocketImplFactory は 1 回しか呼び出せないので、それを実行するテストを 1 つだけ持つ必要があることに注意してください (私が持っているように)、またはそのスパイを保持するための静的シングルトンを作成する必要があります (yuck!)。
次に問題は、ソケットが閉じているかどうかを調べる方法です。Socket と ServerSocket の両方に method がありますが、メソッドisClosed()
が閉じられたかどうかを追跡するためにこれらのクラスの内部のブール値を使用します。SocketImpl インスタンスには、閉じられたかどうかを確認する簡単な方法がありません。(ところで、Socket と ServerSocket の両方が SocketImpl によってサポートされています。「ServerSocketImpl」はありません。)
ありがたいことに、SocketImpl には、それがサポートしている Socket または ServerSocket への参照があります。前述のsetImpl()
メソッドは or を呼び出し、impl.setSocket(this)
orimpl.setServerSocket(this)
を呼び出すことでその参照を取得できます。java.net.SocketImpl#getSocket
java.net.SocketImpl#getServerSocket
繰り返しになりますが、これらのメソッドはパッケージ プライベートであるため、少し反映する必要があります。
private static Socket getSocket(SocketImpl impl) {
try {
Method getSocket = SocketImpl.class.getDeclaredMethod("getSocket");
getSocket.setAccessible(true);
return (Socket) getSocket.invoke(impl);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static ServerSocket getServerSocket(SocketImpl impl) {
try {
Method getServerSocket = SocketImpl.class.getDeclaredMethod("getServerSocket");
getServerSocket.setAccessible(true);
return (ServerSocket) getServerSocket.invoke(impl);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
getSocket/getServerSocket は SocketImplFactory 内で呼び出されないことに注意してください。Socket/ServerSocket は、SocketImpl がそこから返された後にのみ設定するためです。
これで、Socket/ServerSocket について必要なすべてのテストをチェックインするために必要なすべてのインフラストラクチャができました。
for (SocketImpl impl : allSockets) {
assertIsClosed(getSocket(impl));
}
完全なソース コードはこちらです。