blob: 52e3d7944fb628d9904b9f6815d0060b7e6584b1 [file] [log] [blame]
package io.prediction;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.ning.http.client.*;
import com.ning.http.client.extra.ThrottleRequestFilter;
import com.ning.http.client.providers.netty.NettyAsyncHttpProvider;
import org.joda.time.DateTime;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
/**
* The Client class is a full feature abstraction on top of the RESTful PredictionIO REST API interface.
* <p>
* All REST request methods come in both synchronous and asynchronous flavors via method overloading.
* Synchronization methods are also provided via method overloading.
* <p>
* Multiple simultaneous asynchronous requests is made possible by the high performance backend provided by the <a href="https://github.com/AsyncHttpClient/async-http-client">Async Http Client</a>.
*
* @author The PredictionIO Team (<a href="http://prediction.io">http://prediction.io</a>)
* @version 0.6.1
* @since 0.1
*/
public class Client {
// API base URL constant string
private static final String defaultApiUrl = "http://localhost:8000";
private static final String apiFormat = "json";
private static final int defaultThreadLimit = 100;
// HTTP status code
private static final int HTTP_OK = 200;
private static final int HTTP_CREATED = 201;
private static final int HTTP_BAD_REQUEST = 400;
private static final int HTTP_FORBIDDEN = 403;
private static final int HTTP_NOT_FOUND = 404;
//API Url
private String apiUrl;
// Appkey
private String appkey;
// Async HTTP client
private AsyncHttpClientConfig config;
private AsyncHttpClient client;
private JsonParser parser = new JsonParser();
// internal field
private String uid = "";
/**
* Instantiate a PredictionIO RESTful API client using default values for API URL and thread limit.
* <p>
* The default API URL is http://localhost:8000. The default thread limit is 100.
*
* @param appkey the app key that this client will use to communicate with the API
*/
public Client(String appkey) {
this(appkey, Client.defaultApiUrl, Client.defaultThreadLimit);
}
/**
* Instantiate a PredictionIO RESTful API client using default values for API URL.
* <p>
* The default API URL is http://localhost:8000.
*
* @param appkey the app key that this client will use to communicate with the API
* @param apiURL the URL of the PredictionIO API
*/
public Client(String appkey, String apiURL) {
this(appkey, apiURL, Client.defaultThreadLimit);
}
/**
* Instantiate a PredictionIO RESTful API client.
*
* @param appkey the app key that this client will use to communicate with the API
* @param apiURL the URL of the PredictionIO API
* @param threadLimit maximum number of simultaneous threads (connections) to the API
*/
public Client(String appkey, String apiURL, int threadLimit) {
if (apiURL != null) {
this.apiUrl = apiURL;
} else {
this.apiUrl = defaultApiUrl;
}
this.setAppkey(appkey);
// Async HTTP client config
this.config = (new AsyncHttpClientConfig.Builder())
.setAllowPoolingConnection(true)
.setAllowSslConnectionPool(true)
.addRequestFilter(new ThrottleRequestFilter(threadLimit))
.setMaximumConnectionsPerHost(threadLimit)
.setRequestTimeoutInMs(10000)
.setIOThreadMultiplier(threadLimit)
.build();
this.client = new AsyncHttpClient(new NettyAsyncHttpProvider(config), config);
}
/**
* Close all connections associated with this client.
* It is a good practice to always close the client after use.
*/
public void close() {
this.client.close();
}
/**
* Identify the user ID.
* This identified user ID will be used for creating user-action-on-item request or getting item recommendation.
*
* @param uid ID of the User to be identified
*/
public void identify(String uid) {
this.uid = uid;
}
private AsyncHandler<APIResponse> getHandler() {
return new AsyncHandler<APIResponse>() {
private final Response.ResponseBuilder builder = new Response.ResponseBuilder();
public void onThrowable(Throwable t) {
}
public STATE onBodyPartReceived(HttpResponseBodyPart content) throws Exception {
builder.accumulate(content);
return STATE.CONTINUE;
}
public STATE onStatusReceived(HttpResponseStatus status) throws Exception {
builder.accumulate(status);
return STATE.CONTINUE;
}
public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception {
builder.accumulate(headers);
return STATE.CONTINUE;
}
public APIResponse onCompleted() throws Exception {
Response r = builder.build();
return new APIResponse(r.getStatusCode(), r.getResponseBody());
}
};
}
private String[] jsonArrayAsStringArray(JsonArray a) throws ClassCastException {
int l = a.size();
String[] r = new String[l];
for (int i = 0; i < l; i++) {
JsonElement e = a.get(i);
if (e.isJsonNull()) {
r[i] = null;
} else {
r[i] = e.getAsString();
}
}
return r;
}
private int[] jsonArrayAsIntArray(JsonArray a) throws ClassCastException {
int l = a.size();
int[] r = new int[l];
for (int i = 0; i < l; i++) {
r[i] = a.get(i).getAsInt();
}
return r;
}
private double[] jsonArrayAsDoubleArray(JsonArray a) throws ClassCastException {
int l = a.size();
double[] r = new double[l];
for (int i = 0; i < l; i++) {
r[i] = a.get(i).getAsDouble();
}
return r;
}
/**
* Set the app key of this client.
* <p>
* All subsequent requests after this method will use the new app key.
*
* @param appkey the new app key to be used
*/
public void setAppkey(String appkey) {
// Set current API's appkey
this.appkey = appkey;
}
/**
* Set the API URL of this client.
* <p>
* All subsequent requests after this method will use the new API URL.
*
* @param apiUrl the new API URL to be used
*/
public void setApiUrl(String apiUrl) {
// Set current API's appkey
this.apiUrl = apiUrl;
}
/**
* Get status of the API.
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public String getStatus() throws ExecutionException, InterruptedException, IOException {
return (new FutureAPIResponse(this.client.prepareGet(this.apiUrl).execute(this.getHandler()))).get().getMessage();
}
/**
* Get a create user request builder that can be used to add additional user attributes.
*
* @param uid ID of the User to be created
*/
public CreateUserRequestBuilder getCreateUserRequestBuilder(String uid) {
return new CreateUserRequestBuilder(this.apiUrl, this.apiFormat, this.appkey, uid);
}
/**
* Sends an asynchronous create user request to the API.
*
* @param builder an instance of {@link CreateUserRequestBuilder} that will be turned into a request
*/
public FutureAPIResponse createUserAsFuture(CreateUserRequestBuilder builder) throws IOException {
return new FutureAPIResponse(this.client.executeRequest(builder.build(), this.getHandler()));
}
/**
* Sends a synchronous create user request to the API.
*
* @param uid ID of the User to be created
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void createUser(String uid) throws ExecutionException, InterruptedException, IOException {
this.createUser(this.createUserAsFuture(this.getCreateUserRequestBuilder(uid)));
}
/**
* Sends a synchronous create user request to the API.
*
* @param builder an instance of {@link CreateUserRequestBuilder} that will be turned into a request
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void createUser(CreateUserRequestBuilder builder) throws ExecutionException, InterruptedException, IOException {
this.createUser(this.createUserAsFuture(builder));
}
/**
* Synchronize a previously sent asynchronous create user request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#createUserAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void createUser(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status != Client.HTTP_CREATED) {
throw new IOException(message);
}
}
/**
* Sends an asynchronous get user request to the API.
*
* @param uid ID of the User to get
*/
public FutureAPIResponse getUserAsFuture(String uid) throws IOException {
Request request = (new RequestBuilder("GET")).setUrl(this.apiUrl + "/users/" + uid + "." + this.apiFormat).addQueryParameter("pio_appkey", this.appkey).build();
return new FutureAPIResponse(this.client.executeRequest(request, this.getHandler()));
}
/**
* Sends a synchronous get user request to the API.
*
* @param uid ID of the User to get
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public User getUser(String uid) throws ExecutionException, InterruptedException, IOException {
return this.getUser(this.getUserAsFuture(uid));
}
/**
* Synchronize a previously sent asynchronous get user request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#getUserAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public User getUser(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status == Client.HTTP_OK) {
JsonObject messageAsJson = (JsonObject) parser.parse(message);
String uid = messageAsJson.get("pio_uid").getAsString();
User user = new User(uid);
if (messageAsJson.getAsJsonArray("pio_latlng") != null) {
double latlng[] = this.jsonArrayAsDoubleArray(messageAsJson.getAsJsonArray("pio_latlng"));
user.latitude(new Double(latlng[0]));
user.longitude(new Double(latlng[1]));
}
return user;
} else {
throw new IOException(message);
}
}
/**
* Sends an asynchronous delete user request to the API.
*
* @param uid ID of the User to be deleted
*/
public FutureAPIResponse deleteUserAsFuture(String uid) throws IOException {
RequestBuilder builder = new RequestBuilder("DELETE");
builder.setUrl(this.apiUrl + "/users/" + uid + "." + this.apiFormat);
builder.addQueryParameter("pio_appkey", this.appkey);
return new FutureAPIResponse(this.client.executeRequest(builder.build(), this.getHandler()));
}
/**
* Sends a synchronous delete user request to the API.
*
* @param uid ID of the User to be deleted
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void deleteUser(String uid) throws ExecutionException, InterruptedException, IOException {
this.deleteUser(this.deleteUserAsFuture(uid));
}
/**
* Synchronize a previously sent asynchronous delete user request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#deleteUserAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void deleteUser(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status != Client.HTTP_OK) {
throw new IOException(message);
}
}
/**
* Get a create item request builder that can be used to add additional item attributes.
*
* @param iid ID of the Item to be created
* @param itypes array of types of the Item
*/
public CreateItemRequestBuilder getCreateItemRequestBuilder(String iid, String[] itypes) {
return new CreateItemRequestBuilder(this.apiUrl, this.apiFormat, this.appkey, iid, itypes);
}
/**
* Sends an asynchronous create item request to the API.
*
* @param builder an instance of {@link CreateItemRequestBuilder} that will be turned into a request
*/
public FutureAPIResponse createItemAsFuture(CreateItemRequestBuilder builder) throws IOException {
return new FutureAPIResponse(this.client.executeRequest(builder.build(), this.getHandler()));
}
/**
* Sends a synchronous create item request to the API.
*
* @param iid ID of the Item to be created
* @param itypes array of types of the Item
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void createItem(String iid, String[] itypes) throws ExecutionException, InterruptedException, IOException {
this.createItem(this.createItemAsFuture(this.getCreateItemRequestBuilder(iid, itypes)));
}
/**
* Sends a synchronous create item request to the API.
*
* @param builder an instance of {@link CreateItemRequestBuilder} that will be turned into a request
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void createItem(CreateItemRequestBuilder builder) throws ExecutionException, InterruptedException, IOException {
this.createItem(this.createItemAsFuture(builder));
}
/**
* Synchronize a previously sent asynchronous create item request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#createItemAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void createItem(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status != Client.HTTP_CREATED) {
throw new IOException(message);
}
}
/**
* Sends an asynchronous get item request to the API.
*
* @param iid ID of the Item to get
*/
public FutureAPIResponse getItemAsFuture(String iid) throws IOException {
Request request = (new RequestBuilder("GET")).setUrl(this.apiUrl + "/items/" + iid + "." + this.apiFormat).addQueryParameter("pio_appkey", this.appkey).build();
return new FutureAPIResponse(this.client.executeRequest(request, this.getHandler()));
}
/**
* Sends a synchronous get item request to the API.
*
* @param iid ID of the Item to get
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public Item getItem(String iid) throws ExecutionException, InterruptedException, IOException {
return this.getItem(this.getItemAsFuture(iid));
}
/**
* Synchronize a previously sent asynchronous get item request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#getItemAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public Item getItem(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status == Client.HTTP_OK) {
JsonObject messageAsJson = (JsonObject) parser.parse(message);
String iid = messageAsJson.get("pio_iid").getAsString();
String[] itypes = this.jsonArrayAsStringArray(messageAsJson.getAsJsonArray("pio_itypes"));
Item item = new Item(iid, itypes);
if (messageAsJson.get("pio_startT") != null) {
item.startT(new Date(messageAsJson.get("pio_startT").getAsLong()));
}
if (messageAsJson.get("pio_endT") != null) {
item.endT(new Date(messageAsJson.get("pio_endT").getAsLong()));
}
if (messageAsJson.getAsJsonArray("pio_latlng") != null) {
double latlng[] = this.jsonArrayAsDoubleArray(messageAsJson.getAsJsonArray("pio_latlng"));
item.latitude(new Double(latlng[0]));
item.longitude(new Double(latlng[1]));
}
return item;
} else {
throw new IOException(message);
}
}
/**
* Sends an asynchronous delete item request to the API.
*
* @param iid ID of the Item to be deleted
*/
public FutureAPIResponse deleteItemAsFuture(String iid) throws IOException {
RequestBuilder builder = new RequestBuilder("DELETE");
builder.setUrl(this.apiUrl + "/items/" + iid + "." + this.apiFormat);
builder.addQueryParameter("pio_appkey", this.appkey);
return new FutureAPIResponse(this.client.executeRequest(builder.build(), this.getHandler()));
}
/**
* Sends a synchronous delete item request to the API.
*
* @param iid ID of the Item to be deleted
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void deleteItem(String iid) throws ExecutionException, InterruptedException, IOException {
this.deleteItem(this.deleteItemAsFuture(iid));
}
/**
* Synchronize a previously sent asynchronous delete item request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#deleteItemAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void deleteItem(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status != Client.HTTP_OK) {
throw new IOException(message);
}
}
/**
* Get a get top-n recommendations request builder that can be used to add additional request parameters.
*
* Using this method overrides the user ID set by {@link Client#identify}.
*
* @param engine engine name
* @param uid ID of the User whose recommendations will be gotten
* @param n number of top recommendations to get
*/
public ItemRecGetTopNRequestBuilder getItemRecGetTopNRequestBuilder(String engine, String uid, int n) {
return new ItemRecGetTopNRequestBuilder(this.apiUrl, this.apiFormat, this.appkey, engine, uid, n);
}
/**
* Get a get top-n recommendations request builder that can be used to add additional request parameters.
* Identified user ID will be used. See {@link Client#identify}.
*
* @param engine engine name
* @param n number of top recommendations to get
*
* @throws UnidentifiedUserException indicates an unidentified user ID error
*/
public ItemRecGetTopNRequestBuilder getItemRecGetTopNRequestBuilder(String engine, int n) throws UnidentifiedUserException {
if (this.uid == "") {
throw new UnidentifiedUserException("User ID has not been identified. Please call identify(uid) first.");
}
return new ItemRecGetTopNRequestBuilder(this.apiUrl, this.apiFormat, this.appkey, engine, this.uid, n);
}
/**
* Get a get top-n recommendations request builder that can be used to add additional request parameters.
*
* Using this method overrides the user ID set by {@link Client#identify}.
*
* @param engine engine name
* @param uid ID of the User whose recommendations will be gotten
* @param n number of top recommendations to get
* @param attributes array of item attribute names to be returned with the result
*/
public ItemRecGetTopNRequestBuilder getItemRecGetTopNRequestBuilder(String engine, String uid, int n, String[] attributes) {
return (new ItemRecGetTopNRequestBuilder(this.apiUrl, this.apiFormat, this.appkey, engine, uid, n)).attributes(attributes);
}
/**
* Get a get top-n recommendations request builder that can be used to add additional request parameters.
* Identified user ID will be used. See {@link Client#identify}.
*
* @param engine engine name
* @param n number of top recommendations to get
* @param attributes array of item attribute names to be returned with the result
*
* @throws UnidentifiedUserException indicates an unidentified user ID error
*/
public ItemRecGetTopNRequestBuilder getItemRecGetTopNRequestBuilder(String engine, int n, String[] attributes) throws UnidentifiedUserException {
if (this.uid == "") {
throw new UnidentifiedUserException("User ID has not been identified. Please call identify(uid) first.");
}
return (new ItemRecGetTopNRequestBuilder(this.apiUrl, this.apiFormat, this.appkey, engine, this.uid, n)).attributes(attributes);
}
/**
* Sends an asynchronous get recommendations request to the API.
*
* @param builder an instance of {@link ItemRecGetTopNRequestBuilder} that will be turned into a request
*/
public FutureAPIResponse getItemRecTopNAsFuture(ItemRecGetTopNRequestBuilder builder) throws IOException {
return new FutureAPIResponse(this.client.executeRequest(builder.build(), this.getHandler()));
}
/**
* Sends a synchronous get recommendations request to the API.
*
* Using this method overrides the user ID set by {@link Client#identify}.
*
* @param engine engine name
* @param uid ID of the User whose recommendations will be gotten
* @param n number of top recommendations to get
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public String[] getItemRecTopN(String engine, String uid, int n) throws ExecutionException, InterruptedException, IOException {
return this.getItemRecTopN(this.getItemRecTopNAsFuture(this.getItemRecGetTopNRequestBuilder(engine, uid, n)));
}
/**
* Sends a synchronous get recommendations request to the API.
* Identified user ID will be used. See {@link Client#identify}.
*
* @param engine engine name
* @param n number of top recommendations to get
*
* @throws UnidentifiedUserException indicates an unidentified user ID error
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public String[] getItemRecTopN(String engine, int n) throws UnidentifiedUserException, ExecutionException, InterruptedException, IOException {
return this.getItemRecTopN(this.getItemRecTopNAsFuture(this.getItemRecGetTopNRequestBuilder(engine, n)));
}
/**
* Sends a synchronous get recommendations request to the API.
*
* @param builder an instance of {@link ItemRecGetTopNRequestBuilder} that will be turned into a request
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public String[] getItemRecTopN(ItemRecGetTopNRequestBuilder builder) throws ExecutionException, InterruptedException, IOException {
return this.getItemRecTopN(this.getItemRecTopNAsFuture(builder));
}
/**
* Synchronize a previously sent asynchronous get recommendations request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#getItemRecTopNAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public String[] getItemRecTopN(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
// Do not use getStatus/getMessage directly as they do not pass exceptions
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status == Client.HTTP_OK) {
JsonObject messageAsJson = (JsonObject) parser.parse(message);
JsonArray iidsAsJson = messageAsJson.getAsJsonArray("pio_iids");
return this.jsonArrayAsStringArray(iidsAsJson);
} else {
throw new IOException(message);
}
}
/**
* Sends a synchronous get recommendations request to the API.
*
* Using this method overrides the user ID set by {@link Client#identify}.
*
* @param engine engine name
* @param uid ID of the User whose recommendations will be gotten
* @param n number of top recommendations to get
* @param attributes array of item attribute names to be returned with the result
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public Map<String, String[]> getItemRecTopNWithAttributes(String engine, String uid, int n, String[] attributes) throws ExecutionException, InterruptedException, IOException {
return this.getItemRecTopNWithAttributes(this.getItemRecTopNAsFuture(this.getItemRecGetTopNRequestBuilder(engine, uid, n, attributes)));
}
/**
* Sends a synchronous get recommendations request to the API.
* Identified user ID will be used. See {@link Client#identify}.
*
* @param engine engine name
* @param n number of top recommendations to get
* @param attributes array of item attribute names to be returned with the result
*
* @throws UnidentifiedUserException indicates an unidentified user ID error
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public Map<String, String[]> getItemRecTopNWithAttributes(String engine, int n, String[] attributes) throws UnidentifiedUserException, ExecutionException, InterruptedException, IOException {
return this.getItemRecTopNWithAttributes(this.getItemRecTopNAsFuture(this.getItemRecGetTopNRequestBuilder(engine, n, attributes)));
}
/**
* Sends a synchronous get recommendations request to the API.
*
* @param builder an instance of {@link ItemRecGetTopNRequestBuilder} that will be turned into a request
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public Map<String, String[]> getItemRecTopNWithAttributes(ItemRecGetTopNRequestBuilder builder) throws ExecutionException, InterruptedException, IOException {
return this.getItemRecTopNWithAttributes(this.getItemRecTopNAsFuture(builder));
}
/**
* Synchronize a previously sent asynchronous get recommendations request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#getItemRecTopNAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public Map<String, String[]> getItemRecTopNWithAttributes(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
// Do not use getStatus/getMessage directly as they do not pass exceptions
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status == Client.HTTP_OK) {
HashMap<String, String[]> results = new HashMap();
JsonObject messageAsJson = (JsonObject) parser.parse(message);
for (Map.Entry<String, JsonElement> member : messageAsJson.entrySet()) {
results.put(member.getKey(), this.jsonArrayAsStringArray(member.getValue().getAsJsonArray()));
}
return results;
} else {
throw new IOException(message);
}
}
/**
* Get a get top-n similar items request builder that can be used to add additional request parameters.
*
* @param engine engine name
* @param iid ID of the Item
* @param n number of top similar items to get
*/
public ItemSimGetTopNRequestBuilder getItemSimGetTopNRequestBuilder(String engine, String iid, int n) {
return new ItemSimGetTopNRequestBuilder(this.apiUrl, this.apiFormat, this.appkey, engine, iid, n);
}
/**
* Get a get top-n similar items request builder that can be used to add additional request parameters.
*
* @param engine engine name
* @param iid ID of the Item
* @param n number of top similar items to get
* @param attributes array of item attribute names to be returned with the result
*/
public ItemSimGetTopNRequestBuilder getItemSimGetTopNRequestBuilder(String engine, String iid, int n, String[] attributes) {
return new ItemSimGetTopNRequestBuilder(this.apiUrl, this.apiFormat, this.appkey, engine, iid, n).attributes(attributes);
}
/**
* Sends an asynchronous get similar items request to the API.
*
* @param builder an instance of {@link ItemSimGetTopNRequestBuilder} that will be turned into a request
*/
public FutureAPIResponse getItemSimTopNAsFuture(ItemSimGetTopNRequestBuilder builder) throws IOException {
return new FutureAPIResponse(this.client.executeRequest(builder.build(), this.getHandler()));
}
/**
* Sends a synchronous get similar items request to the API.
*
* @param engine engine name
* @param iid ID of the Item
* @param n number of top recommendations to get
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public String[] getItemSimTopN(String engine, String iid, int n) throws ExecutionException, InterruptedException, IOException {
return this.getItemSimTopN(this.getItemSimTopNAsFuture(this.getItemSimGetTopNRequestBuilder(engine, iid, n)));
}
/**
* Sends a synchronous get similar items request to the API.
*
* @param builder an instance of {@link ItemSimGetTopNRequestBuilder} that will be turned into a request
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public String[] getItemSimTopN(ItemSimGetTopNRequestBuilder builder) throws ExecutionException, InterruptedException, IOException {
return this.getItemSimTopN(this.getItemSimTopNAsFuture(builder));
}
/**
* Synchronize a previously sent asynchronous get similar items request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#getItemSimTopNAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public String[] getItemSimTopN(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
// Do not use getStatus/getMessage directly as they do not pass exceptions
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status == Client.HTTP_OK) {
JsonObject messageAsJson = (JsonObject) parser.parse(message);
JsonArray iidsAsJson = messageAsJson.getAsJsonArray("pio_iids");
return this.jsonArrayAsStringArray(iidsAsJson);
} else {
throw new IOException(message);
}
}
/**
* Sends a synchronous get similar items request to the API.
*
* @param engine engine name
* @param iid ID of the Item
* @param n number of top recommendations to get
* @param attributes array of item attribute names to be returned with the result
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public Map<String, String[]> getItemSimTopNWithAttributes(String engine, String iid, int n, String[] attributes) throws ExecutionException, InterruptedException, IOException {
return this.getItemSimTopNWithAttributes(this.getItemSimTopNAsFuture(this.getItemSimGetTopNRequestBuilder(engine, iid, n, attributes)));
}
/**
* Sends a synchronous get similar items request to the API.
*
* @param builder an instance of {@link ItemSimGetTopNRequestBuilder} that will be turned into a request
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public Map<String, String[]> getItemSimTopNWithAttributes(ItemSimGetTopNRequestBuilder builder) throws ExecutionException, InterruptedException, IOException {
return this.getItemSimTopNWithAttributes(this.getItemSimTopNAsFuture(builder));
}
/**
* Synchronize a previously sent asynchronous get similar items request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#getItemSimTopNAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public Map<String, String[]> getItemSimTopNWithAttributes(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
// Do not use getStatus/getMessage directly as they do not pass exceptions
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status == Client.HTTP_OK) {
HashMap<String, String[]> results = new HashMap();
JsonObject messageAsJson = (JsonObject) parser.parse(message);
for (Map.Entry<String, JsonElement> member : messageAsJson.entrySet()) {
results.put(member.getKey(), this.jsonArrayAsStringArray(member.getValue().getAsJsonArray()));
}
return results;
} else {
throw new IOException(message);
}
}
/**
* Get a user-action-on-item request builder that can be used to add additional request parameters.
*
* Using this method overrides the user ID set by {@link Client#identify}.
*
* @param uid ID of the User of this action
* @param action action name
* @param iid ID of the Item of this action
*/
public UserActionItemRequestBuilder getUserActionItemRequestBuilder(String uid, String action, String iid) {
UserActionItemRequestBuilder builder = new UserActionItemRequestBuilder(this.apiUrl, this.apiFormat, this.appkey, action, uid, iid);
return builder;
}
/**
* Get a user-action-on-item request builder that can be used to add additional request parameters.
* Identified user ID will be used. See {@link Client#identify}.
*
* @param action action name
* @param iid ID of the Item of this action
*
* @throws UnidentifiedUserException indicates an unidentified user ID error
*/
public UserActionItemRequestBuilder getUserActionItemRequestBuilder(String action, String iid) throws UnidentifiedUserException {
if (this.uid == "") {
throw new UnidentifiedUserException("User ID has not been identified. Please call identify(uid) first.");
}
UserActionItemRequestBuilder builder = new UserActionItemRequestBuilder(this.apiUrl, this.apiFormat, this.appkey, action, this.uid, iid);
return builder;
}
/**
* Sends an asynchronous user-action-on-item request to the API.
*
* Using this method overrides the user ID set by {@link Client#identify}.
*
* @param uid ID of the User of this action
* @param action action name
* @param iid ID of the Item of this action
*/
public FutureAPIResponse userActionItemAsFuture(String uid, String action, String iid) throws IOException {
return this.userActionItemAsFuture(this.getUserActionItemRequestBuilder(uid, action, iid));
}
/**
* Sends an asynchronous user-action-on-item request to the API.
* Identified user ID will be used. See {@link Client#identify}.
*
* @param action action name
* @param iid ID of the Item of this action
*
* @throws UnidentifiedUserException indicates an unidentified user ID error
*/
public FutureAPIResponse userActionItemAsFuture(String action, String iid) throws UnidentifiedUserException, IOException {
return this.userActionItemAsFuture(this.getUserActionItemRequestBuilder(action, iid));
}
/**
* Sends an asynchronous user-action-on-item request to the API.
*
* @param builder an instance of {@link UserActionItemRequestBuilder} that will be turned into a request
*/
public FutureAPIResponse userActionItemAsFuture(UserActionItemRequestBuilder builder) throws IOException {
return new FutureAPIResponse(this.client.executeRequest(builder.build(), this.getHandler()));
}
/**
* Sends a synchronous user-action-on-item request to the API.
*
* Using this method overrides the user ID set by {@link Client#identify}.
*
* @param uid ID of the User of this action
* @param action action name
* @param iid ID of the Item of this action
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void userActionItem(String uid, String action, String iid) throws ExecutionException, InterruptedException, IOException {
this.userActionItem(this.userActionItemAsFuture(this.getUserActionItemRequestBuilder(uid, action, iid)));
}
/**
* Sends a synchronous user-action-on-item request to the API.
* Identified user ID will be used. See {@link Client#identify}.
*
* @param action action name
* @param iid ID of the Item of this action
*
* @throws UnidentifiedUserException indicates an unidentified user ID error
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void userActionItem(String action, String iid) throws UnidentifiedUserException, ExecutionException, InterruptedException, IOException {
this.userActionItem(this.userActionItemAsFuture(this.getUserActionItemRequestBuilder(action, iid)));
}
/**
* Sends a synchronous user-rate-item action request to the API.
*
* @param builder an instance of {@link UserActionItemRequestBuilder} that will be turned into a request
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void userActionItem(UserActionItemRequestBuilder builder) throws ExecutionException, InterruptedException, IOException {
this.userActionItem(this.userActionItemAsFuture(builder));
}
/**
* Synchronize a previously sent asynchronous user-action-on-item request.
*
* @param response an instance of {@link FutureAPIResponse} returned from {@link Client#userRateItemAsFuture}
*
* @throws ExecutionException indicates an error in the HTTP backend
* @throws InterruptedException indicates an interruption during the HTTP operation
* @throws IOException indicates an error from the API response
*/
public void userActionItem(FutureAPIResponse response) throws ExecutionException, InterruptedException, IOException {
int status = response.get().getStatus();
String message = response.get().getMessage();
if (status != Client.HTTP_CREATED) {
throw new IOException(message);
}
}
}