From 2b2a47a0086eab52b09e5425e75cafb488e51074 Mon Sep 17 00:00:00 2001 From: Maurice Laveaux Date: Thu, 22 May 2014 14:47:03 +0200 Subject: Updated the Requesters to throw RateLimitException. * The exception contains the reset timestamp in unix time. --- src/io/AbstractRequester.java | 22 +++++++++++++++------- src/io/BearerRequester.java | 17 ++++++++++------- src/io/OAuthRequester.java | 11 +++++++++-- src/io/RateLimitException.java | 18 ++++++++++++++++++ src/io/Requester.java | 3 ++- 5 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 src/io/RateLimitException.java diff --git a/src/io/AbstractRequester.java b/src/io/AbstractRequester.java index 239e75d..913e9bd 100644 --- a/src/io/AbstractRequester.java +++ b/src/io/AbstractRequester.java @@ -1,6 +1,8 @@ package io; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.google.gson.JsonParser; import java.io.FileNotFoundException; import java.io.IOException; @@ -24,28 +26,35 @@ public abstract class AbstractRequester implements Requester { private static final String API_URL = "https://api.twitter.com/"; @Override - public Response getJSON(String resource) throws IOException { + public Response getJSON(String resource) + throws IOException, RateLimitException { HttpURLConnection conn = open(buildUrl(resource)); try { preconnect(conn); JsonElement resp = getResponseAsJson(conn); - - if(resp.isJsonObject() && resp.getAsJsonObject().has("errors")) { + + if (resp.isJsonObject() && resp.getAsJsonObject().has("errors")) { /* print response to stderr for debugging */ String errors = resp.getAsJsonObject().get("errors").toString(); getLogger().log(Level.INFO, "Request failed: {0}", errors); } + + // TODO: what if there is an internal server error? Technically we // should always treat that as "don't know what the server thinks". if (conn.getResponseCode() != 200) { + if (conn.getResponseCode() == 429) { + //TODO: wait for real time and not 15 minutes. + throw new RateLimitException(Integer.parseInt(conn.getHeaderField("X-Rate-Limit-Reset"))); + } // TODO: print more helpful details throw new IOException("Unexpected response code"); - } + } int rateLimit = Integer.parseInt(conn.getHeaderField("X-Rate-Limit-Limit")); int rateLimitRemaining = Integer.parseInt(conn.getHeaderField("X-Rate-Limit-Remaining")); int rateLimitReset = Integer.parseInt(conn.getHeaderField("X-Rate-Limit-Reset")); - + return new Response(resp, rateLimit, rateLimitRemaining, rateLimitReset); } finally { conn.disconnect(); @@ -116,11 +125,10 @@ public abstract class AbstractRequester implements Requester { throw new IOException("Failed to fetch response"); } IOUtils.copy(is, writer, Charsets.UTF_8); - + JsonParser parser = new JsonParser(); return parser.parse(writer.toString()); } - /** * Prepare the request before it gets send. diff --git a/src/io/BearerRequester.java b/src/io/BearerRequester.java index 11776fc..8af38f8 100644 --- a/src/io/BearerRequester.java +++ b/src/io/BearerRequester.java @@ -1,14 +1,13 @@ package io; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; +import java.util.logging.Level; +import java.util.logging.Logger; import org.apache.commons.io.Charsets; -import org.json.JSONException; -import org.json.JSONObject; import support.ConsumerKeySecret; /** @@ -88,9 +87,13 @@ public class BearerRequester extends AbstractRequester { @Override public boolean isValid() throws IOException { - // NOTE: this actually contributes to the ratelimit (12/minute) - // TODO: find alternative that does not hit the ratelimit - Response obj = getJSON("application/rate_limit_status"); - return !obj.getResp().getAsJsonObject().has("errors"); + try { + // NOTE: this actually contributes to the ratelimit (12/minute) + // TODO: find alternative that does not hit the ratelimit + Response obj = getJSON("application/rate_limit_status"); + return obj.getResp().getAsJsonObject().has("errors"); + } catch (RateLimitException ex) { + return false; + } } } diff --git a/src/io/OAuthRequester.java b/src/io/OAuthRequester.java index 3621186..9cde429 100644 --- a/src/io/OAuthRequester.java +++ b/src/io/OAuthRequester.java @@ -3,6 +3,8 @@ package io; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URLConnection; +import java.util.logging.Level; +import java.util.logging.Logger; import oauth.signpost.OAuth; import oauth.signpost.OAuthConsumer; import oauth.signpost.basic.DefaultOAuthConsumer; @@ -136,7 +138,12 @@ public class OAuthRequester extends AbstractRequester { public boolean isValid() throws IOException { // NOTE: this actually contributes to the ratelimit (12/minute) // TODO: find alternative that does not hit the ratelimit - Response obj = getJSON("application/rate_limit_status"); - return !obj.getResp().getAsJsonObject().has("errors"); + Response obj; + try { + obj = getJSON("application/rate_limit_status"); + return !obj.getResp().getAsJsonObject().has("errors"); + } catch (RateLimitException ex) { + return false; + } } } diff --git a/src/io/RateLimitException.java b/src/io/RateLimitException.java new file mode 100644 index 0000000..2bb2624 --- /dev/null +++ b/src/io/RateLimitException.java @@ -0,0 +1,18 @@ +package io; + +/** + * The exception that will be thrown when a ratelimit has been hit. + * + * @author Maurice Laveaux + */ +public class RateLimitException extends Exception { + private final long rateLimitReset; + + public RateLimitException(long resetTime) { + rateLimitReset = resetTime; + } + + public long getResetTime() { + return rateLimitReset; + } +} diff --git a/src/io/Requester.java b/src/io/Requester.java index 3dd09f3..11e0bb1 100644 --- a/src/io/Requester.java +++ b/src/io/Requester.java @@ -20,8 +20,9 @@ public interface Requester { * @param resource The REST resource. * @return A JSON object resulting from the request. * @throws java.io.IOException on error fetching the resource. + * @throws io.RateLimitException on ratelimit errors. */ - public Response getJSON(String resource) throws IOException; + public Response getJSON(String resource) throws IOException, RateLimitException; /** * Tests whether this instance can dispatch requests. -- cgit v1.2.1