com.factual.driver.Factual Maven / Gradle / Ivy
Show all versions of factual-java-driver Show documentation
package com.factual.driver;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;
import org.json.JSONArray;
import com.google.api.client.auth.oauth.OAuthHmacSigner;
import com.google.api.client.auth.oauth.OAuthParameters;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.UrlEncodedContent;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.common.collect.Maps;
import com.google.common.io.Closeables;
/**
* Represents the public Factual API. Supports running queries against Factual
* and inspecting the response. Supports the same levels of authentication
* supported by Factual's API.
*
* @author aaron
*/
public class Factual {
private static final String DRIVER_HEADER_TAG = "factual-java-driver-v1.8.9-android";
private static final String DEFAULT_HOST_HEADER = "api.v3.factual.com";
private String factHome = "http://api.v3.factual.com/";
private String host = DEFAULT_HOST_HEADER;
private final String key;
private final OAuthHmacSigner signer;
private boolean debug = false;
private int readTimeout = -1;
private int connectionTimeout = -1;
private StreamHandler debugHandler = null;
private Logger logger = null;
/**
* Constructor. Represents your authenticated access to Factual.
*
* @param key
* your oauth key.
* @param secret
* your oauth secret.
*/
public Factual(String key, String secret) {
this(key, secret, false);
}
/**
* Constructor. Represents your authenticated access to Factual.
*
* @param key
* your oauth key.
* @param secret
* your oauth secret.
* @param debug
* whether or not this is in debug mode
*/
public Factual(String key, String secret, boolean debug) {
this.key = key;
this.signer = new OAuthHmacSigner();
this.signer.clientSharedSecret = secret;
debug(debug);
}
private HeaderProvider headerProvider = null;
public void setHeaderProvider(HeaderProvider headerProvider) {
this.headerProvider = headerProvider;
}
/**
* Change the base URL at which to contact Factual's API. This may be useful
* if you want to talk to a test or staging server.
*
* Example value: http://staging.api.v3.factual.com/t/
*
* @param urlBase
* the base URL at which to contact Factual's API.
*/
public synchronized void setFactHome(String urlBase) {
this.factHome = urlBase;
}
/**
* Change the host header value for a request to Factual's API.
*
* @param host
* the host header value for a request to Factual's API.
*/
public synchronized void setRequestHost(String host) {
this.host = host;
}
/**
* Runs a read query against the specified Factual table.
*
* @param tableName
* the name of the table you wish to query (e.g., "places")
* @param query
* the read query to run against table.
* @return the response of running query against Factual.
*/
public ReadResponse fetch(String tableName, Query query) {
return new ReadResponse(getInternal(urlForFetch(tableName), query.toUrlParams()));
}
/**
* Runs a read query against the specified Factual table.
*
* @param tableName
* the name of the table you wish to query (e.g., "places")
* @param factualId
* the factual id
* @param query
* the read query to run against table.
* @return the response of running query against Factual.
*/
public RowResponse fetchRow(String tableName, String factualId, RowQuery query) {
return new RowResponse(getInternal(urlForFetchRow(tableName, factualId), query.toUrlParams()));
}
/**
* Runs a read query against the specified Factual table.
*
* @param tableName
* the name of the table you wish to query (e.g., "places")
* @param factualId
* the factual id
* @return the response of running query against Factual.
*/
public RowResponse fetchRow(String tableName, String factualId) {
return fetchRow(tableName, factualId, new RowQuery());
}
protected static String urlForResolve(String tableName) {
return "t/" + tableName + "/resolve";
}
protected static String urlForMatch(String tableName) {
return "t/" + tableName + "/match";
}
protected static String urlForFetch(String tableName) {
return "t/" + tableName;
}
protected static String urlForFetchRow(String tableName, String factualId) {
return "t/" + tableName + "/" + factualId;
}
protected static String urlForFacets(String tableName) {
return "t/" + tableName + "/facets";
}
protected static String urlForGeocode() {
return "places/geocode";
}
protected static String urlForGeopulse() {
return "geopulse/context";
}
/**
* Reverse geocodes by returning a response containing the address nearest to
* the given point.
*
* @param point
* the point for which the nearest address is returned
* @return the response of running a reverse geocode query for point
* against Factual.
*/
public ReadResponse reverseGeocode(Point point) {
return new ReadResponse(getInternal(urlForGeocode(),
new Geocode(point).toUrlParams()));
}
/**
* Runs a facet read against the specified Factual table.
*
*
* @param tableName
* the name of the table you wish to query for facets (e.g.,
* "places")
* @param facet
* the facet query to run against table
* @return the response of running facet against Factual.
*/
public FacetResponse fetch(String tableName, FacetQuery facet) {
return new FacetResponse(getInternal(urlForFacets(tableName), facet.toUrlParams()));
}
/**
* Runs a submit input against the specified Factual table.
*
* @param tableName
* the name of the table you wish to submit updates for (e.g.,
* "places")
* @param factualId
* the factual id on which the submit is run
* @param submit
* the submit parameters to run against table
* @param metadata
* the metadata to send with information on this request
* @return the response of running submit against Factual.
*/
public SubmitResponse submit(String tableName, String factualId,
Submit submit, Metadata metadata) {
return submitCustom("t/" + tableName + "/" + factualId + "/submit", submit,
metadata);
}
/**
* Runs a insert to add a row against the specified Factual table.
* Insert is virtually identical to submit. The only difference between the
* two is that insert will not search for potential duplicate rows first.
*
* @param tableName
* the name of the table you wish to insert the add for (e.g.,
* "places")
* @param insert
* the insert parameters to run against table
* @param metadata
* the metadata to send with information on this request
* @return the response of running insert against Factual.
*/
public InsertResponse insert(String tableName, Insert insert,
Metadata metadata) {
return insertCustom("t/" + tableName + "/insert", insert, metadata);
}
/**
* Runs a insert against the specified Factual table. Insert is
* virtually identical to submit. The only difference between the two is that
* insert will not search for potential duplicate rows first.
*
* @param tableName
* the name of the table you wish to insert updates for (e.g.,
* "places")
* @param factualId
* the factual id on which the insert is run
* @param insert
* the insert parameters to run against table
* @param metadata
* the metadata to send with information on this request
* @return the response of running insert against Factual.
*/
public InsertResponse insert(String tableName, String factualId,
Insert insert, Metadata metadata) {
return insertCustom("t/" + tableName + "/" + factualId + "/insert", insert,
metadata);
}
/**
* Runs a clear against the specified Factual table. Insert is
* virtually identical to submit. The only difference between the two is that
* insert will not search for potential duplicate rows first.
*
* @param tableName
* the name of the table you wish to insert updates for (e.g.,
* "places")
* @param factualId
* the factual id on which the insert is run
* @param clear
* the clear parameters to run against table
* @param metadata
* the metadata to send with information on this request
* @return the response of running insert against Factual.
*/
public ClearResponse clear(String tableName, String factualId, Clear clear,
Metadata metadata) {
return clearCustom("t/" + tableName + "/" + factualId + "/clear", clear,
metadata);
}
/**
* Runs a submit to add a row against the specified Factual table.
*
* @param tableName
* the name of the table you wish to submit the add for (e.g.,
* "places")
* @param submit
* the submit parameters to run against table
* @param metadata
* the metadata to send with information on this request
* @return the response of running submit against Factual.
*/
public SubmitResponse submit(String tableName, Submit submit,
Metadata metadata) {
return submitCustom("t/" + tableName + "/submit", submit, metadata);
}
/**
* Flags a row a closed business in the specified Factual table.
*
* @param tableName
* the name of the table you wish to flag a closed business in (e.g.,
* "places")
* @param factualId
* the factual id that is the closed business
* @param metadata
* the metadata to send with information on this request
*
* @return the response from flagging a closed row.
*/
public FlagResponse flagClosed(String tableName, String factualId,
Metadata metadata) {
return flagCustom(urlForFlag(tableName, factualId), "closed", null, null, metadata);
}
/**
* @deprecated
* Deprecated method for flagging a duplicate. Please use the newer method
* which takes a _preferredFactualId_ instead.
*
* @param tableName
* the name of the table you wish to flag a duplicate for (e.g.,
* "places")
* @param factualId
* the factual id that is the duplicate
* @param metadata
* the metadata to send with information on this request
*
* @return the response from flagging a duplicate row.
*/
@Deprecated
public FlagResponse flagDuplicate(String tableName, String factualId,
Metadata metadata) {
return flagCustom(urlForFlag(tableName, factualId), "duplicate", null, null, metadata);
}
/**
* Flag a business as being a duplicate of another.
*
* @param tableName
* the name of the table you wish to flag a duplicate for (e.g.,
* "places")
* @param factualId
* the factual id of the duplicate row
* @param preferredFactualId
* the factual id that is preferred of the two duplicates to persist.
* @param metadata
* the metadata to send with information on this request
*
* @return the response from flagging a duplicate row.
*/
public FlagResponse flagDuplicate(String tableName, String factualId, String preferredFactualId,
Metadata metadata) {
return flagCustom(urlForFlag(tableName, factualId), "duplicate", preferredFactualId, null, metadata);
}
protected static String urlForFlag(String tableName, String factualId) {
return "t/" + tableName + "/" + factualId + "/flag";
}
/**
* @deprecated
* Deprecated method for flagging a row as inaccurate. Please use the newer method
* which takes a List of inaccurate field names instead.
*
* @param tableName
* the name of the table you wish to flag an inaccurate row for
* (e.g., "places")
* @param factualId
* the factual id that is inaccurate
* @param metadata
* the metadata to send with information on this request
*
* @return the response from flagging an inaccurate row.
*/
@Deprecated
public FlagResponse flagInaccurate(String tableName, String factualId,
Metadata metadata) {
return flagCustom(urlForFlag(tableName, factualId), "inaccurate", null, null, metadata);
}
/**
* Flag a row as being inaccurate.
*
* @param tableName
* the name of the table you wish to flag an inaccurate row for
* (e.g., "places")
* @param factualId
* the factual id that is inaccurate
* @param fields
* a List of fields (by name) which you konw to contain inaccurate
* data, however for which you don't actually have the proper corrections.
* If you have actual corrections, please use the submit API to update
* the row.
* @param metadata
* the metadata to send with information on this request
* @return
*/
public FlagResponse flagInaccurate(String tableName, String factualId, List fields,
Metadata metadata) {
return flagCustom(urlForFlag(tableName, factualId), "inaccurate", null, fields, metadata);
}
/**
* Flags a row as inappropriate in the specified Factual table.
*
* @param tableName
* the name of the table you wish to flag an inappropriate row for
* (e.g., "places")
* @param factualId
* the factual id that is inappropriate
* @param metadata
* the metadata to send with information on this request
*
* @return the response from flagging an inappropriate row.
*/
public FlagResponse flagInappropriate(String tableName, String factualId,
Metadata metadata) {
return flagCustom(urlForFlag(tableName, factualId), "inappropriate", null, null,
metadata);
}
/**
* Flags a row as non-existent in the specified Factual table.
*
* @param tableName
* the name of the table you wish to flag a non-existent row for
* (e.g., "places")
* @param factualId
* the factual id that is non-existent
* @param metadata
* the metadata to send with information on this request
*
* @return the response from flagging a non-existent row.
*/
public FlagResponse flagNonExistent(String tableName, String factualId,
Metadata metadata) {
return flagCustom(urlForFlag(tableName, factualId), "nonexistent", null, null, metadata);
}
/**
* Flags a row as having being relocated, where its new location is an existing
* record, identified by preferredFactualId. If there is no record corresponding
* to the relocated business, use the submit API to update the record's address
* instead.
*
* @param tableName
* @param factualId
* @param preferredFactualId
* @param metadata
* @return
*/
public FlagResponse flagRelocated(String tableName, String factualId, String preferredFactualId,
Metadata metadata) {
return flagCustom(urlForFlag(tableName, factualId), "relocated", preferredFactualId, null, metadata);
}
/**
* Flags a row as spam in the specified Factual table.
*
* @param tableName
* the name of the table you wish to flag a row as spam (e.g.,
* "places")
* @param factualId
* the factual id that is spam
* @param metadata
* the metadata to send with information on this request
*
* @return the response from flagging a row as spam.
*/
public FlagResponse flagSpam(String tableName, String factualId,
Metadata metadata) {
return flagCustom(urlForFlag(tableName, factualId), "spam", null, null, metadata);
}
/**
* Flags a row as problematic in the specified Factual table.
*
* @param tableName
* the name of the table for which you wish to flag as problematic
* (e.g., "places")
* @param factualId
* the factual id that has a problem other than duplicate,
* inaccurate, inappropriate, non-existent, or spam.
* @param metadata
* the metadata to send with information on this request
*
* @return the response from flagging a row as problematic.
*/
public FlagResponse flagOther(String tableName, String factualId,
Metadata metadata) {
return flagCustom(urlForFlag(tableName, factualId), "other", null, null, metadata);
}
/**
* Runs a GET request against the specified endpoint path, using the given
* parameters and your OAuth credentials. Returns the raw response body
* returned by Factual.
*
* The necessary URL base will be automatically prepended to path. If
* you need to change it, e.g. to make requests against a development instance
* of the Factual service, please see {@link #setFactHome(String)}.
*
* @param path
* the endpoint path to run the request against. example: "t/places"
* @param queryParams
* the query string parameters to send with the request. do not
* encode or escape these; that will be done automatically.
* @return the response body from running this GET request against Factual.
* @throws FactualApiException
* if something goes wrong.
*/
public String get(String path, Map queryParams) {
return getInternal(path, queryParams).getContent();
}
private InternalResponse getInternal(String path, Map queryParams) {
return request(new RawReadRequest(path, queryParams));
}
/**
*
* Runs a "GET" request against the path specified using the parameter string
* specified and your Oauth token.
*
* @param path
* the path to run the request against
* @param params
* the url-encoded parameter string to send with the request
* @return the response body from running this GET request against Factual.
*/
public String get(String path, String params) {
return request(new SimpleGetRequest(path, params)).getContent();
}
/**
* Runs a POST request against the specified endpoint path, using the given
* parameters and your OAuth credentials. Returns the raw response body
* returned by Factual.
*
* The necessary URL base will be automatically prepended to path. If
* you need to change it, e.g. to make requests against a development instance
* of the Factual service, please see {@link #setFactHome(String)}.
*
* @param path
* the endpoint path to run the request against. example: "t/places"
* @param queryParams
* the query parameters to send with the request. send null or empty
* to specify none. do not encode or escape these; that will be done
* automatically.
* @param postContent
* the POST content parameters to send with the request. do not
* encode or escape these; that will be done automatically.
* @return the response body from running this POST request against Factual.
* @throws FactualApiException
* if something goes wrong.
*/
public String post(String path, Map queryParams,
Map postContent) {
return postInternal(path, queryParams, postContent).getContent();
}
private InternalResponse postInternal(String path, Map queryParams,
Map postContent) {
return requestPost(new RawReadRequest(path, queryParams, postContent));
}
public FactualStream stream(String tableName, DiffsQuery diff, DiffsCallback cb) {
return stream(new DiffsRequest(urlForFetch(tableName) + "/diffs", diff.toUrlParams(), cb), "GET", true);
}
public DiffsResponse fetch(String tableName, DiffsQuery diff) {
DiffsResponse resp = new DiffsResponse();
FactualStream stream = stream(tableName, diff, resp);
stream.start();
return resp;
}
private ClearResponse clearCustom(String root, Clear clear, Metadata metadata) {
Map params = Maps.newHashMap();
params.putAll(metadata.toUrlParams());
params.putAll(clear.toUrlParams());
// Oauth library currently doesn't support POST body content.
InternalResponse resp = postInternal(root, params, new HashMap());
return new ClearResponse(resp);
}
private InsertResponse insertCustom(String root, Insert insert,
Metadata metadata) {
Map params = Maps.newHashMap();
params.putAll(metadata.toUrlParams());
params.putAll(insert.toUrlParams());
// Oauth library currently doesn't support POST body content.
InternalResponse resp = postInternal(root, params, new HashMap());
return new InsertResponse(resp);
}
private SubmitResponse submitCustom(String root, Submit submit,
Metadata metadata) {
Map params = Maps.newHashMap();
params.putAll(metadata.toUrlParams());
params.putAll(submit.toUrlParams());
// Oauth library currently doesn't support POST body content.
InternalResponse resp = postInternal(root, params, new HashMap());
return new SubmitResponse(resp);
}
private FlagResponse flagCustom(String root, String flagType, String preferredFactualId, Listfields, Metadata metadata) {
Map params = Maps.newHashMap();
params.putAll(metadata.toUrlParams());
params.put("problem", flagType);
if (preferredFactualId != null){
params.put("preferred", preferredFactualId);
}
if (fields != null){
params.put("fields", new JSONArray(fields).toString());
}
// Oauth library currently doesn't support POST body content.
InternalResponse resp = postInternal(root, params, new HashMap());
return new FlagResponse(resp);
}
/**
* Use this to send all queued reads as a multi request
*
* @return response for a multi request
*/
public MultiResponse sendRequests(MultiRequest multiRequest) {
Map multi = Maps.newHashMap();
Map queries = multiRequest.getQueries();
for (Entry entry : queries.entrySet()) {
RequestImpl fullQuery = entry.getValue();
String url = "/" + fullQuery.toUrlString();
multi.put(entry.getKey(), url);
}
String json = JsonUtil.toJsonStr(multi);
Map params = Maps.newHashMap();
params.put("queries", json);
InternalResponse internalResp = getInternal("multi", params);
MultiResponse resp = new MultiResponse(queries);
resp.setJson(internalResp.getContent());
return resp;
}
/**
* Asks Factual to resolve the Places entity for the attributes specified by
* query.
*
* Returns the read response from a Factual Resolve request, which includes
* all records that are potential matches.
*
* @param query
* the Resolve query to run against Factual's Places table.
* @return the response from Factual for the Resolve request.
*/
public ResolveResponse resolves(ResolveQuery query) {
return resolves("places", query);
}
/**
* Asks Factual to resolve the entity for the attributes specified by
* query.
*
* Returns the read response from a Factual Resolve request, which includes
* all records that are potential matches.
*
* @param query
* the Resolve query to run against Factual's Places table.
* @return the response from Factual for the Resolve request.
*/
public ResolveResponse resolves(String tableId, ResolveQuery query) {
query.allCandidates();
return fetch(tableId, query);
}
/**
* Asks Factual to resolve the Places entity for the attributes specified by
* query. Returns a record representing the resolved entity if
* Factual successfully identified the entity with full confidence, or null if
* the entity was not resolved.
*
* @param query
* a Resolve query with partial attributes for an entity.
* @return a record representing the resolved entity if Factual successfully
* identified the entity with full confidence, or null if the entity
* was not resolved.
*/
public Map resolve(ResolveQuery query) {
return resolve("places", query);
}
/**
* Asks Factual to resolve the entity for the attributes specified by
* query. Returns a record representing the resolved entity if
* Factual successfully identified the entity with full confidence, or null if
* the entity was not resolved.
*
* @param query
* a Resolve query with partial attributes for an entity.
* @return a record representing the resolved entity if Factual successfully
* identified the entity with full confidence, or null if the entity
* was not resolved.
*/
public Map resolve(String tableId, ResolveQuery query) {
ResolveResponse resp = resolves(tableId, query);
if (resp.isResolved())
return resp.getResolved();
else
return null;
}
/**
* Asks Factual to resolve the entity for the attributes specified by
* query. Returns a factual id for the resolved entity if Factual
* successfully identified the entity with full confidence, or null if the
* entity was not resolved.
*
* @param query
* a Match query with partial attributes for an entity.
* @return a factual id for the resolved entity if Factual successfully
* identified the entity with full confidence, or null if the entity
* was not resolved.
*/
public String match(String tableId, MatchQuery query) {
ResolveResponse resp = new ResolveResponse(request(new ReadRequest(
urlForMatch(tableId), query.toUrlParams())));
if (resp.getData().size() > 0)
return String.valueOf(resp.getData().get(0).get("factual_id"));
else
return null;
}
/**
* Asks Factual to resolve the entity for the attributes specified by
* query, within the table called tableName.
*
* Returns the read response from a Factual Resolve request, which includes
* all records that are potential matches.
*
* Each result record will include a confidence score ("similarity"),
* and a flag indicating whether Factual decided the entity is the correct
* resolved match with a high degree of accuracy ("resolved").
*
* There will be 0 or 1 entities returned with "resolved"=true. If there was a
* full match, it is guaranteed to be the first record in the response.
*
* @param tableName
* the name of the table to resolve within.
* @param query
* a Resolve query with partial attributes for an entity.
* @return the response from Factual for the Resolve request.
*/
public ResolveResponse fetch(String tableName, ResolveQuery query) {
return new ResolveResponse(request(new ReadRequest(
urlForResolve(tableName), query.toUrlParams())));
}
/**
*
* @param tableName
* @param factualId
* The Factual ID of an entity that should be boosted in search
* results.
* @return
*/
public BoostResponse boost(String tableName, String factualId) {
return boost(tableName, factualId, null, null);
}
/**
*
* @param tableName
* @param factualId
* The Factual ID of an entity that should be boosted in search
* results.
* @param search
* full-text-search query parameter value for q from a read request.
* @return
*/
public BoostResponse boost(String tableName, String factualId, String search) {
return boost(tableName, factualId, search, null);
}
/**
*
* @param tableName
* @param factualId
* The Factual ID of an entity that should be boosted in search
* results.
* @param search
* full-text-search query parameter value for q from a read request.
* @param user
* An arbitrary token for correlating read and boost requests to a
* single app/session/etc. Factual does not use this token to track
* users. The function of this information is only to help evaluate
* how a boost relates to a search.
* @return
*/
public BoostResponse boost(String tableName, String factualId, String search, String user) {
Boost boost = new Boost(factualId);
if (search != null)
boost.search(search);
if (user != null)
boost.user(user);
return boost(tableName, boost);
}
/**
*
* @param tableName
* @param factualId
* @param query
* Perform boost request against settings from an existing read query
* @return
*/
public BoostResponse boost(String tableName, String factualId, Query query) {
return boost(tableName, new Boost(factualId, query));
}
/**
*
* @param tableName
* @param boost
* @return
*/
public BoostResponse boost(String tableName, Boost boost) {
// Oauth library currently doesn't support POST body content.
InternalResponse resp = postInternal("t/" + tableName + "/boost", boost.toUrlParams(), new HashMap());
return new BoostResponse(resp);
}
public SchemaResponse schema(String tableName) {
Map params = Maps.newHashMap();
return new SchemaResponse(getInternal(urlForSchema(tableName), params));
}
private String urlForSchema(String tableName) {
return "t/" + tableName + "/schema";
}
private InternalResponse request(Request query) {
return request(query, true);
}
private InternalResponse request(Request query, boolean useOAuth) {
return request(query, "GET", useOAuth);
}
private InternalResponse requestPost(Request query) {
return requestPost(query, true);
}
private InternalResponse requestPost(Request query, boolean useOAuth) {
return request(query, "POST", useOAuth);
}
private InternalResponse request(Request fullQuery, String requestMethod,
boolean useOAuth) {
String urlStr = factHome + fullQuery.toUrlString();
BufferedReader br = null;
try {
HttpRequest request = createRequest(urlStr, fullQuery, requestMethod, useOAuth);
// get the response
HttpResponse resp = request.execute();
InternalResponse internalResponse = new InternalResponse(resp, fullQuery.getLineCallback());
/*
br = new BufferedReader(new InputStreamReader(resp
.getContent()));
String line = null;
StringBuffer sb = new StringBuffer();
LineCallback cb = fullQuery.getLineCallback();
while ((line = br.readLine()) != null) {
if (cb != null)
cb.onLine(line);
sb.append(line);
}
return sb.toString();
*/
return internalResponse;
} catch (HttpResponseException e) {
throw new FactualApiException(e).requestUrl(urlStr)
.requestMethod(requestMethod).response(e.getStatusCode(), e.getMessage());
} catch (IOException e) {
throw new FactualApiException(e).requestUrl(urlStr).requestMethod(
requestMethod);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} finally {
Closeables.closeQuietly(br);
}
}
private FactualStream stream(Request fullQuery, String requestMethod,
boolean useOAuth) {
String urlStr = factHome + fullQuery.toUrlString();
BufferedReader br = null;
try {
HttpRequest request = createRequest(urlStr, fullQuery, requestMethod, useOAuth);
// get the response
br = new BufferedReader(new InputStreamReader(request.execute()
.getContent()));
return new FactualStream(br, fullQuery.getLineCallback());
} catch (HttpResponseException e) {
throw new FactualApiException(e).requestUrl(urlStr)
.requestMethod(requestMethod).response(e.getStatusCode(), e.getMessage());
} catch (IOException e) {
throw new FactualApiException(e).requestUrl(urlStr).requestMethod(
requestMethod);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
private HttpRequest createRequest(String urlStr, Request fullQuery, String requestMethod,
boolean useOAuth) throws GeneralSecurityException, IOException {
Map postData = fullQuery.getPostData();
GenericUrl url = new GenericUrl(urlStr);
if (debug) {
fullQuery.printDebug();
logger = Logger.getLogger(HttpTransport.class.getName());
logger.removeHandler(debugHandler);
logger.setLevel(Level.ALL);
logger.addHandler(debugHandler);
}
// Configure OAuth request params
OAuthParameters params = new OAuthParameters();
params.consumerKey = key;
params.computeNonce();
params.computeTimestamp();
params.signer = signer;
// generate the signature
params.computeSignature(requestMethod, url);
// make the request
HttpTransport transport = new NetHttpTransport();
HttpRequestFactory f = null;
if (useOAuth) {
f = transport.createRequestFactory(params);
} else {
f = transport.createRequestFactory();
}
HttpRequest request = null;
if ("POST".equals(requestMethod))
if (postData == null)
request = f.buildPostRequest(url, null);
else
request = f.buildPostRequest(url, new UrlEncodedContent(postData));
else
request = f.buildGetRequest(url);
if (readTimeout != -1)
request.setReadTimeout(readTimeout);
if (connectionTimeout != -1)
request.setConnectTimeout(connectionTimeout);
HttpHeaders headers = new HttpHeaders();
headers.set("X-Factual-Lib", DRIVER_HEADER_TAG);
headers.set("Host", host);
if (headerProvider != null) {
Map info = headerProvider.getHeaders();
for (String key : info.keySet()) {
headers.set(key, info.get(key));
}
}
request.setHeaders(headers);
return request;
}
/**
* Set the driver in or out of debug mode.
*
* @param debug
* whether or not this is in debug mode
*/
public synchronized void debug(boolean debug) {
this.debug = debug;
if (debug && debugHandler == null) {
debugHandler = new StreamHandler(System.out, new SimpleFormatter());
debugHandler.setLevel(Level.ALL);
}
}
/**
* Sets the timeout in milliseconds to establish a connection or {@code 0} for
* an infinite timeout.
*
*
* By default it is 20000 (20 seconds).
*
*/
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
/**
* Sets the timeout in milliseconds to read data from an established
* connection or {@code 0} for an infinite timeout.
*
*
* By default it is 20000 (20 seconds).
*
*/
public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
}
protected static interface Request {
public String toUrlString();
public Map getPostData();
public Response getResponse(InternalResponse resp);
public void printDebug();
public LineCallback getLineCallback();
}
protected static class ReadRequest extends RequestImpl {
public ReadRequest(String path, Map params) {
super(path, params);
}
@Override
public Response getResponse(InternalResponse resp) {
return new ReadResponse(resp);
}
}
/**
* Represents a request against Factual given a path and parameters
*
* @author brandon
*
*/
protected static abstract class RequestImpl implements Request {
private final Map params;
private final Map postData;
private final String path;
protected LineCallback cb;
public RequestImpl(String path, Map params) {
this(path, params, new HashMap());
}
public RequestImpl(String path, Map params,
Map postData) {
this.path = path;
this.params = params;
this.postData = postData;
}
public Map getRequestParams() {
return params;
}
@Override
public String toUrlString() {
return UrlUtil.toUrl(path, getRequestParams());
}
@Override
public Map getPostData() {
return postData;
}
@Override
public abstract Response getResponse(InternalResponse resp);
@Override
public void printDebug() {
System.out.println("=== " + path + " ===");
System.out.println("Parameters:");
if (params != null) {
for (String key : params.keySet()) {
System.out.println(key + ": " + params.get(key));
}
}
}
@Override
public LineCallback getLineCallback() {
return cb;
}
}
protected static class ResolveRequest extends RequestImpl {
public ResolveRequest(String path, Map params) {
super(path, params);
}
@Override
public Response getResponse(InternalResponse resp) {
return new ResolveResponse(resp);
}
}
protected static class FacetRequest extends RequestImpl {
public FacetRequest(String path, Map params) {
super(path, params);
}
@Override
public Response getResponse(InternalResponse resp) {
return new FacetResponse(resp);
}
}
protected static class SchemaRequest extends RequestImpl {
public SchemaRequest(String path, Map params) {
super(path, params);
}
@Override
public Response getResponse(InternalResponse resp) {
return new SchemaResponse(resp);
}
}
protected static class DiffsRequest extends RequestImpl {
public DiffsRequest(String path, Map params, final DiffsCallback cb) {
super(path, params);
this.cb = new LineCallback() {
@Override
public void onLine(String line) {
cb.onDiff(line);
}
};
}
@Override
public Response getResponse(InternalResponse resp) {
return new RawReadResponse(resp);
}
}
protected static class RawReadRequest extends RequestImpl {
public RawReadRequest(String path, Map params) {
super(path, params);
}
public RawReadRequest(String path, Map params,
Map postData) {
super(path, params, postData);
}
@Override
public Response getResponse(InternalResponse resp) {
return new RawReadResponse(resp);
}
}
protected static class SimpleGetRequest implements Request {
private final String path;
private final String params;
public SimpleGetRequest(String path, String params) {
this.path = path;
this.params = params;
}
@Override
public String toUrlString() {
return UrlUtil.toUrl(path, params);
}
@Override
public Map getPostData() {
return null;
}
@Override
public Response getResponse(InternalResponse resp) {
return new RawReadResponse(resp);
}
@Override
public void printDebug() {
System.out.println("=== " + path + " ===");
System.out.println("Parameters:");
System.out.println(params);
}
@Override
public LineCallback getLineCallback() {
return null;
}
}
}