Thursday, September 10, 2009

You're in Timeout

I recently had an issue with some code I wrote to connect to a service with a "rest like" interface that returns XML data given a simple query over a HTTP GET request.

I used that java.net.URL class to send the query and retrieve the results as follows..

private InputStream executeQuery(String query) {

InputStream rawResults = null;

try {
query = baseUrl + "?query=" + query;
if(LOG.isDebugEnabled()) {
LOG.debug("Full Query : " + query);
}
URL request = new URL(query);
rawResults = request.openStream();
}
catch (IOException e) {
LOG.warn(e.getMessage(), e);
}

return rawResults;
}

If the host on which this service runs is down (or doesn't exist) before URL.openStream() gets invoked, a java.net.UnknownHostException is quickly thrown (within about 4000 milliseconds), the catch block is executed, and the process goes on.

However, if the service goes down after the socket connection is made, but before the data is read back from the service, this method call to URL.openStream will block indefinitely by default.

Eric Burke has written a post defining this problem and a simple solution, which can be found here. The solution is to set the read timeout on the URLConnection so that you can break after so many milliseconds if you are't getting the data back.

For example:

private InputStream executeQuery(String query) {

InputStream rawResults = null;

try {
query = baseUrl + "?query=" + query;
if(LOG.isDebugEnabled()) {
LOG.debug("Full Query : " + query);
}
URL url = new URL(query);
URLConnection conn = url.openConnection();

// Seems set to 4000ms by default already.
conn.setConnectTimeout( 4000 );

// Set to 0 by default, T/O never occurs!
conn.setReadTimeout( 10000 );

rawResults = conn.getInputStream();

} catch (java.net.UnknownHostException e) {
LOG.warn("Could not make connection to
host within the allotted connectTimeout", e);
} catch (java.net.SocketTimeoutException e) {
LOG.warn("Could finish reading data from the
socket within the allotted readTimeout", e);
}

return rawResults;
}

Now as the catch blocks indicate, the host can go down, or the service can be unresponsive before or during the read, and we'll cut out after our chosen timeout.

No comments: