/* * Copyright (c) 2009-2010, Metaweb Technologies, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY METAWEB TECHNOLOGIES AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL METAWEB * TECHNOLOGIES OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ package com.freebase.api; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import com.freebase.json.JSON; import static com.freebase.json.JSON.o; import static com.freebase.json.JSON.a; /** * This class is the main access point to the Freebase API. * * You can obtain two flavors of this class, one that operates * on freebase.com and one that operates on sandbox-freebase.com. * * sandbox-freebase.com is a scratch system that works exactly like * freebase.com but its data get reset every week and brought back * in synch with freebase.com. * * If your programs write data in Freebase, we highly encourage * to use sandbox first for extensive testing before turning the * writes over to Freebase.com. * * Use <code>Freebase.getFreebase()</code> to obtain a instance * of this class that talks to freebase.com and <code>Freebase.getFreebaseSandbox()</code> * to obtain one that talks to sandbox-freebase.com. * */ public class Freebase extends JSONTransport { private static final String FREEBASE_API_URL = "http://www.freebase.com"; private static final String FREEBASE_SANDBOX_API_URL = "http://www.sandbox-freebase.com"; private static final String LOGIN_API = "/api/account/login"; private static final String MQLREAD_API = "/api/service/mqlread"; private static final String MQLWRITE_API = "/api/service/mqlwrite"; private static final String SEARCH_API = "/api/service/search"; private static final String GEOSEARCH_API = "/api/service/geosearch"; private static final String UPLOAD_API = "/api/service/upload"; private static final String BLOB_API_PREFIX = "/api/trans/"; private static final String TOPIC_API_PREFIX = "/experimental/topic/"; // -------------------- Static Creators ------------------------- /** * Obtain an instance of this class that connects to the API on freebase.com * NOTE: if your program writes data, we highly encourage you to test it * against the sandbox instance first. */ public static Freebase getFreebase() { return new Freebase(FREEBASE_API_URL); } /** * Obtain an instance of this class that connects to the API on sandbox-freebase.com */ public static Freebase getFreebaseSandbox() { return new Freebase(FREEBASE_SANDBOX_API_URL); } // -------------------- Private Constructor ------------------------- private final String host; private String credential_cookie = null; private Freebase(String host) { this.host = host; } // --------------------- Sign in/out ----------------------------- /** * For API calls that write data in Freebase, your requests must be authenticated. * Calling this method with your Freebase username and password will * provide enough credentials for your write API calls to be authorized. * NOTE: if you don't have a Freebase account, you can get one for free at * <a href="http://www.freebase.com">freebase.com</a> */ public void sign_in(String username, String password) { try { HttpClient httpclient = new DefaultHttpClient(); HttpPost post = new HttpPost(host + LOGIN_API); List<NameValuePair> formparams = new ArrayList<NameValuePair>(); formparams.add(new BasicNameValuePair("username", username)); formparams.add(new BasicNameValuePair("password", password)); post.setEntity(new UrlEncodedFormEntity(formparams, "UTF-8")); post.setHeader("Content-Type","application/x-www-form-urlencoded"); HttpResponse response = httpclient.execute(post); this.credential_cookie = response.getFirstHeader("Set-Cookie").getValue(); } catch (ClientProtocolException e) { throw new FreebaseException(e); } catch (IOException e) { throw new FreebaseException(e); } catch (IllegalStateException e) { throw new FreebaseException(e); } catch (ClassCastException e) { throw new FreebaseException(e); } } /** * Sign out and clean up the authentication credentials. */ public void sign_out() { this.credential_cookie = null; } // ---------------------- Abstraction Implementations ------------------ /** * Create a fully qualified URL for the web service identified by the given path * (this is used to switch between freebase.com and sandbox-freebase.com) */ protected String getURL(String path) { return this.host + path; } /** * Add authentication credentials to the HTTP request method */ protected void sign(HttpRequestBase method) { if (this.credential_cookie == null) throw new FreebaseException("Can't sign the request since there are no authentication credentials. Have you signed in yet?"); method.setHeader("Cookie", this.credential_cookie); } // -------------------- MQL read ------------------------------- public JSON mqlread(JSON query) { return mqlread(query, null, null); } /** * http://www.freebase.com/docs/web_services/mqlread */ public JSON mqlread(JSON query, JSON envelope, JSON params) { if (query == null) throw new FreebaseException("Query can't be null"); if (envelope == null) envelope = o(); envelope.put("query", jsonize(query)); envelope.put("escape",false); List<NameValuePair> qparams = transform_params(params); qparams.add(new BasicNameValuePair("query",envelope.toString())); return invoke(MQLREAD_API,qparams); } public JSON mqlread_multiple(JSON queries) { return mqlread_multiple(queries, null, null); } /** * http://www.freebase.com/docs/web_services/mqlread */ @SuppressWarnings("unchecked") public JSON mqlread_multiple(JSON queries, JSON envelopes, JSON params) { if (queries == null || queries.object().keySet().size() == 0) throw new FreebaseException("Query can't be null or empty"); if (envelopes == null) envelopes = o(); JSON q = o(); for (Object entry : queries.object().entrySet()) { Map.Entry<String,JSON> e = (Map.Entry<String,JSON>) entry; JSON envelope = envelopes.get(e.getKey()); if (envelope == null) envelope = o(); envelope.put("query", jsonize(e.getValue())); envelope.put("escape",false); q.put(e.getKey(), envelope); } List<NameValuePair> qparams = transform_params(params); qparams.add(new BasicNameValuePair("queries",q.toString())); return invoke(MQLREAD_API,qparams); } // ---------------------- Search ----------------------------- /** * http://www.freebase.com/docs/web_services/search */ public JSON search(String query) { return search(query, null); } /** * http://www.freebase.com/docs/web_services/search */ public JSON search(String query, JSON options) { if (query == null || query.trim().length() == 0) throw new FreebaseException("You must provide a string to search"); List<NameValuePair> qparams = transform_params(options); qparams.add(new BasicNameValuePair("query",query)); qparams.add(new BasicNameValuePair("format","json")); return invoke(SEARCH_API,qparams); } // ---------------------- GeoSearch ------------------------------ /** * http://www.freebase.com/docs/geosearch */ public JSON geosearch(String query) { return geosearch(query, null); } /** * http://www.freebase.com/docs/geosearch */ public JSON geosearch(String location, JSON options) { if (location == null || location.trim().length() == 0) throw new FreebaseException("You must provide a location to geoearch"); List<NameValuePair> qparams = transform_params(options); qparams.add(new BasicNameValuePair("location",location)); qparams.add(new BasicNameValuePair("format","json")); return invoke(GEOSEARCH_API,qparams); } // ----------------------- Blobs ---------------------------------- public String get_blob(String id) { return get_blob(id,null); } /** * http://www.freebase.com/docs/web_services/trans_raw * http://www.freebase.com/docs/web_services/trans_blurb */ public String get_blob(String id, JSON options) { if (id == null || id.trim().length() == 0) throw new FreebaseException("You must provide the id of the blob you want"); String path = BLOB_API_PREFIX; String mode = (options != null && options.has("mode")) ? options.get("mode").string() : "raw"; if ("raw".equals(mode) || "unsafe".equals(mode) || "blurb".equals(mode)) { path += mode; } else { throw new FreebaseException("Invalid mode; must be 'raw' or 'blurb' or 'unsafe'"); } path += id; List<NameValuePair> qparams = transform_params(options); String url = host + path + "?" + URLEncodedUtils.format(qparams, "UTF-8"); try { HttpClient httpclient = new DefaultHttpClient(); HttpRequestBase method = new HttpGet(url); method.setHeader("X-Requested-With", "1"); HttpResponse response = httpclient.execute(method); HttpEntity entity = response.getEntity(); if (entity != null) { return stringify(new InputStreamReader(entity.getContent(),"UTF-8")); } else { throw new FreebaseException("Response was empty"); } } catch (ClientProtocolException e) { throw new FreebaseException(e); } catch (IOException e) { throw new FreebaseException(e); } catch (IllegalStateException e) { throw new FreebaseException(e); } catch (ClassCastException e) { throw new FreebaseException(e); } } // --------------------- Topics -------------------------------------- /** * http://www.freebase.com/docs/topic_api */ public JSON get_topic(String id) { return get_topic(id, null); } /** * http://www.freebase.com/docs/topic_api */ public JSON get_topic(String id, JSON options) { if (id == null || id.trim().length() == 0) throw new FreebaseException("Invalid Topic ID"); if (id.indexOf(',') > -1) throw new FreebaseException("Use get_topic_multi if you want to retrieve multiple topics"); return topic(a(id),options).get(id); } /** * http://www.freebase.com/docs/topic_api */ public JSON get_topic_multiple(JSON ids) { return get_topic_multiple(ids, null); } /** * http://www.freebase.com/docs/topic_api */ public JSON get_topic_multiple(JSON ids, JSON options) { if (ids == null || ids.array().size() == 0) throw new FreebaseException("You must provide a non-empty array of Topic IDs"); return topic(ids,options); } private JSON topic(JSON ids, JSON options) { String path = TOPIC_API_PREFIX; String mode = (options != null && options.has("mode")) ? options.get("mode").string() : "standard"; if ("standard".equals(mode) || "basic".equals(mode)) { path += mode; } else { throw new FreebaseException("Invalid mode; must be 'basic' or 'standard'"); } List<NameValuePair> qparams = transform_params(options); qparams.add(new BasicNameValuePair("id",join(ids,","))); return invoke(path,qparams); } // --------------------- MQL write --------------------------------------- /** * http://www.freebase.com/docs/web_services/mqlwrite */ public JSON mqlwrite(JSON query) { return mqlwrite(query,null,null); } /** * http://www.freebase.com/docs/web_services/mqlwrite */ public JSON mqlwrite(JSON query, JSON envelope, JSON params) { if (this.credential_cookie == null) throw new FreebaseException("Can't write without being signed in"); if (query == null) throw new FreebaseException("Query can't be null"); if (envelope == null) envelope = o(); envelope.put("query", jsonize(query)); envelope.put("escape",false); List<NameValuePair> qparams = transform_params(params); qparams.add(new BasicNameValuePair("query",envelope.toString())); return post(MQLWRITE_API,qparams,true); } // --------------------- Upload --------------------------------------------- /** * http://www.freebase.com/docs/web_services/upload */ public JSON upload(String content, String media_type) { return upload(content,media_type,null); } /** * http://www.freebase.com/docs/web_services/upload */ public JSON upload(String content, String media_type, JSON options) { if (this.credential_cookie == null) throw new FreebaseException("Can't write without being signed in"); if (content == null || content.trim().length() == 0) throw new FreebaseException("You must specify what content to upload"); if (media_type == null || media_type.trim().length() == 0) throw new FreebaseException("You must specify a media type for the content to upload"); Map<String,String> headers = new HashMap<String,String>(); headers.put("content-type", media_type); List<NameValuePair> qparams = transform_params(options); String url = getURL(UPLOAD_API) + "?" + URLEncodedUtils.format(qparams, "UTF-8"); return post(url, headers, content, true); } }