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
No more socket black hole sucking up resources:)
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
- Set up a custom socket factory:
Socket.setSocketImplFactory(new SocketImplFactory() {
@Override
public SocketImpl createSocketImpl() { return new NicerSocksSocketImpl(); }
}); - 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 ...
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;
}
}