diff options
Diffstat (limited to 'src/io/AbstractRequester.java')
-rw-r--r-- | src/io/AbstractRequester.java | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/src/io/AbstractRequester.java b/src/io/AbstractRequester.java new file mode 100644 index 0000000..bdd7052 --- /dev/null +++ b/src/io/AbstractRequester.java @@ -0,0 +1,140 @@ +package io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.logging.Logger; +import org.apache.commons.io.Charsets; +import org.apache.commons.io.IOUtils; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Performs an API Request. + * + * @author Peter Wu + */ +public abstract class AbstractRequester implements Requester { + + private static final String API_URL = "https://api.twitter.com/"; + + @Override + public JSONObject getJSON(String resource) throws IOException { + return getJSON(resource, true); + } + + @Override + public JSONObject getJSONRelax(String resource) throws IOException { + return getJSON(resource, false); + } + + private JSONObject getJSON(String resource, boolean checkStatusCode) + throws IOException { + HttpURLConnection conn = open(buildUrl(resource)); + preconnect(conn); + JSONObject resp = getResponseAsJson(conn); + /* print response to stderr for debugging */ + if (resp.has("errors")) { + try { + String errors = resp.get("errors").toString(); + getLogger().info("Request failed: " + errors); + } catch (JSONException ex) { + } + } + // TODO: what if there is an internal server error? Technically we + // should always treat that as "don't know what the server thinks". + if (checkStatusCode && conn.getResponseCode() != 200) { + // TODO: print more helpful details + throw new IOException("Unexpected response code"); + } + return resp; + } + + protected final URL buildUrl(String resource) throws IOException { + String spec = API_URL; + String[] split = resource.split("\\?", 2); + boolean isPreV1; + isPreV1 = resource.startsWith("oauth/") || resource.startsWith("oauth2/"); + if (!isPreV1 && !resource.startsWith("1")) { + // manual inspection shows that at least oauth/ and oauth2/ do not + // have a version prefixed. Do not add version if already given + // (e.g. "1/account/rate_limit_status"). + spec += "1.1/"; + } + // path part + spec += split[0]; + if (!isPreV1) { + // not sure about the history, but oauth{,2}/ don't have this. + spec += ".json"; + } + + // append parameters after path + if (split.length >= 2) { + spec += "?" + split[1]; + } + + return new URL(spec); + } + + /** + * Opens a connection to the URL. + * + * @param url The URL to open a connection to. + * @return a connection that can be used for sending requests. + * @throws java.io.IOException on failure to open a connection. + */ + protected final HttpURLConnection open(URL url) throws IOException { + getLogger().fine("Opening: " + url); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + // set default param: fail if no response within 5 seconds + conn.setReadTimeout(5000); + return conn; + } + + /** + * Reads the response body from a connection (in JSON format). + * + * @param conn An open connection to which a request was already made. + * @return A JSON object as parsed from the response. + * @throws java.io.IOException if the response cannot be retrieved or if the + * response does not contain well-formed JSON. + */ + protected final JSONObject getResponseAsJson(HttpURLConnection conn) + throws IOException { + StringWriter writer = new StringWriter(); + InputStream is; + try { + is = conn.getInputStream(); + } catch (IOException ex) { + /* 404 (FileNotFoundException) */ + is = conn.getErrorStream(); + } + // this could happen if the URL was severly malformed + if (is == null) { + throw new IOException("Failed to fetch response"); + } + IOUtils.copy(is, writer, Charsets.UTF_8); + try { + return new JSONObject(writer.toString()); + } catch (JSONException ex) { + // treat JSON errors as if an I/O error occurred + throw new IOException(ex); + } + } + + /** + * Prepare the request before it gets send. + * + * @param conn A connection for the request. + * @throws java.io.IOException on failing to prepare the request. + */ + protected abstract void preconnect(URLConnection conn) + throws IOException; + + private Logger getLogger() { + return Logger.getLogger(getClass().getName()); + } +} |