Finding out what network sockets are open in the current Java VM

14,702

Solution 1

I was able to hook into java.net.Socket and java.net.ServerSocket and spy all new instances of those classes. The complete code can be seen in the source repository. Here is an overview of the approach:

When a Socket or ServerSocket is instantiated, the first thing in its constructor is a call to setImpl() which instantiates the object which really implements the socket functionality. The default implementation is an instance of java.net.SocksSocketImpl, but it's possible to override that by setting a custom java.net.SocketImplFactory through java.net.Socket#setSocketImplFactory and java.net.ServerSocket#setSocketFactory.

This is complicated a bit by all implementations of java.net.SocketImpl being package-private, but with a little bit of reflection that's not too hard:

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);
    }
}

The SocketImplFactory implementation for spying on all sockets as they are created looks something like this:

    final List<SocketImpl> allSockets = Collections.synchronizedList(new ArrayList<SocketImpl>());
    ServerSocket.setSocketFactory(new SocketImplFactory() {
        public SocketImpl createSocketImpl() {
            SocketImpl socket = newSocketImpl();
            allSockets.add(socket);
            return socket;
        }
    });

Note that setSocketFactory/setSocketImplFactory can be called only once, so you either need to have only one test which does that (like I have it), or you must create a static singleton (yuck!) for holding that spy.

Then the question is that that how to find out whether the socket is closed? Both Socket and ServerSocket have a method isClosed(), but that uses a boolean internal to those classes for keeping track of whether it was closed - the SocketImpl instance does not have an easy way of checking whether it was closed. (BTW, both Socket and ServerSocket are backed by a SocketImpl - there is no "ServerSocketImpl".)

Thankfully the SocketImpl has a reference to the Socket or ServerSocket which it is backing. The aforementioned setImpl() method calls impl.setSocket(this) or impl.setServerSocket(this), and it's possible to get that reference back by calling java.net.SocketImpl#getSocket or java.net.SocketImpl#getServerSocket.

Once again those methods are package-private, so a little bit of reflection is needed:

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);
    }
}

Note that getSocket/getServerSocket may not be called inside the SocketImplFactory, because Socket/ServerSocket sets them only after the SocketImpl is returned from there.

Now there is all the infrastructure necessary for checking in our tests whatever we want about the Socket/ServerSocket:

    for (SocketImpl impl : allSockets) {
        assertIsClosed(getSocket(impl));
    }

The full source code is here.

Solution 2

I haven't tried it myself, but the JavaSpecialists newsletter presents a similar problem:

http://www.javaspecialists.eu/archive/Issue169.html

At the bottom, he describes an approach using AspectJ. You could probably put the pointcut around the constructor that creates the socket, and have code that registers socket creation there.

Share:
14,702
Esko Luontola
Author by

Esko Luontola

Programmer (using TDD/BDD) Interaction Designer (using GUIDe+GDD)

Updated on June 07, 2022

Comments

  • Esko Luontola
    Esko Luontola almost 2 years

    I'm writing an end-to-end test that my Java program releases all of its resources - threads, server sockets, client sockets. It's a library, so releasing resources by exiting the JVM is not an option. Testing the releasing of threads was easy, because you can ask a ThreadGroup for all threads in it, but I haven't yet found a good way to get a list of all network sockets that the current JVM is using.

    Is there some way to get from a JVM the list of all client and server sockets, similar to netstat? I'm using Netty with OIO (i.e. java.net.ServerSocket and java.net.Socket) on Java 7. The solution needs to work on both Windows and Linux.

    My first preference would be to ask it from the JVM using pure Java. I tried to look for an MX Bean or similar, but did not find any.

    Another option might be to connect to the JVM's profiling/debugging APIs and ask for all instances of Socket and ServerSocket, but I don't know how to do that and whether it can be done without native code (AFAIK, JVMTI is native-only). Also, it shouldn't make the tests slow (even my slowest end-to-end test is just 0.5 seconds, which includes starting another JVM process).

    If interrogating the JVM doesn't work, a third option would be to create a design which tracks all sockets as they are created. This has the disadvantage of having a possibility of missing some place where sockets are created. Since I'm using Netty, it seems implementable by wrapping ChannelFactory and using a ChannelGroup.