Google
 
Web unafbapune.blogspot.com

Monday, September 22, 2008

 

No more infinitely blocking socket ?

One common problem Java programmer faces is the direct or indirect use of socket that could block forever. If a socket connect or read operation blocks with an infinite timeout, there is basically nothing that can interrupt the blocking thread besides closing the socket. Not Thread.interrupt(), nor even Thread.stop() would help. Gaining access to the underlying socket could, however, be difficult, considering the socket could be buried deep down inside some third party library, such as a JDBC driver.

Attending the NFJS session, Busy Java Developer's Guide to JDK Hacking by Ted Neward yesterday, a solution came to my mind when Ted demonstrated how easy it was to slip in my own classes ahead of the JDK's. Why not simply extend the existing JDK socket implementation with an additional system property to limit or default the socket timeout to some sensible values ? See an example implementation below. To make this work, simply
  1. Set up a custom socket factory:
    Socket.setSocketImplFactory(new SocketImplFactory() {
    @Override
    public SocketImpl createSocketImpl() { return new NicerSocksSocketImpl(); }
    });
  2. Slip in the implementation class and configure the socket timeout when starting up the JVM:
    java -Xbootclasspath/p:"/path/to/custom/jdk/classes" -DSO_TIMEOUT=1000 ...
Now if a socket timeout is not specified by the individual application or library, the blocking operation would be limited to a maximum of 1,000ms before throwing up.

No more socket black hole sucking up resources:)
package java.net;
import java.io.IOException;

public class NicerSocksSocketImpl extends SocksSocketImpl {
private final int defaultSoTimeout;

public NicerSocksSocketImpl() { this.defaultSoTimeout = defaultSoTimeout(); }

public NicerSocksSocketImpl(String server, int port) {
super(server, port);
this.defaultSoTimeout = defaultSoTimeout();
}

public NicerSocksSocketImpl(Proxy proxy) {
super(proxy);
this.defaultSoTimeout = defaultSoTimeout();
}

private int defaultSoTimeout() {
String defaultSoTimeoutStr = System.getProperty("SO_TIMEOUT");
try {
return Integer.parseInt(defaultSoTimeoutStr);
} catch(RuntimeException ex) {
return 5000; // default to 5 seconds
}
}

@Override
protected void connect(SocketAddress endpoint, int timeout) throws IOException {
super.connect(endpoint, timeout == 0 ? defaultSoTimeout : timeout);
}

@Override
protected void connect(String host, int port) throws UnknownHostException, IOException {
setTimeoutIfNecessary();
super.connect(host, port);
}

@Override
protected void connect(InetAddress address, int port) throws IOException {
setTimeoutIfNecessary();
super.connect(address, port);
}

private void setTimeoutIfNecessary() throws SocketException {
int timeout = super.getTimeout();

if (timeout == 0)
super.setOption(SO_TIMEOUT, defaultSoTimeout);
return;
}
}

Comments: Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?