summaryrefslogtreecommitdiff
path: root/src/io/AbstractRequester.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/io/AbstractRequester.java')
-rw-r--r--src/io/AbstractRequester.java140
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());
+ }
+}