com.gravityrd.recengclient.webshop.GravityClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-client Show documentation
Show all versions of java-client Show documentation
Gravity Java client to access recommendation engine.
package com.gravityrd.recengclient.webshop;
/**
* The com.gravityrd.recengclient.webshop.GravityClient and related classes are used to connect to and communicate with the
* Gravity recommendation engine.
*/
import com.gravityrd.receng.web.webshop.jsondto.GravityEvent;
import com.gravityrd.receng.web.webshop.jsondto.GravityItem;
import com.gravityrd.receng.web.webshop.jsondto.GravityItemRecommendation;
import com.gravityrd.receng.web.webshop.jsondto.GravityRecEngException;
import com.gravityrd.receng.web.webshop.jsondto.GravityRecommendationContext;
import com.gravityrd.receng.web.webshop.jsondto.GravityScenario;
import com.gravityrd.receng.web.webshop.jsondto.GravityUser;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* The com.gravityrd.recengclient.webshop.GravityClient class can be used to send events, item and user information to
* the recommendation engine and get recommendations.
*
* Example usage:
*
*
* public com.gravityrd.recengclient.webshop.GravityClient createGravityClient() {
* com.gravityrd.recengclient.webshop.GravityClient client = new com.gravityrd.recengclient.webshop.GravityClient();
* client.setRemoteUrl("https://-.gravityrd-services.com/grrec--war/WebshopServlet");
* client.setUserName("sampleUser");
* client.setPassword("samplePasswd");
* return client;
* }
* com.gravityrd.recengclient.webshop.GravityClient client = createGravityClient();
* GravityRecommendationContext context = new GravityRecommendationContext();
* context.numberLimit = 1;
* context.scenarioId = "ITEM_PAGE";
* client.getItemRecommendation("user1", context);
*
*/
@SuppressWarnings({ "unused", "WeakerAccess" })
public final class GravityClient {
protected static final ObjectMapper mapper = new ObjectMapper();
private static final Charset UTF8 = Charset.forName("UTF-8");
static {
mapper.getFactory().configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
mapper.getFactory().configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
}
/**
* The version info of the client.
*/
@SuppressWarnings("FieldCanBeLocal")
private final String VERSION = "1.4.0";
/**
* The URL of the server side interface. It has no default value, must be specified.
*/
private String remoteUrl;
/**
* The timeout for the operations in millisecs. The default value is 3000 millisecs.
*/
private int readTimeout = 3000;
/**
* The user name for the http authenticated connection. Leave it blank in case of
* connection without authentication.
*/
private String userName;
/**
* The password for the http authenticated connection. Leave it blank in case of
* connection without authentication.
*/
private String password;
/**
* Query the list of available recommendation scenarios for the backend
* @return array of scenarios
* @throws GravityRecEngException cannot process answer
* @throws IOException cannot connect to server
*/
public GravityScenario[] getScenarioInformation() throws GravityRecEngException, IOException {
return (GravityScenario[]) sendRequest("scenarioInfo", null, null, true, GravityScenario[].class);
}
private Object sendRequest(String methodName, Map queryStringParams, Object requestBody, boolean hasAnswer, Class answerClass) throws GravityRecEngException, IOException {
HttpURLConnection connection = sendRequest(methodName, queryStringParams, requestBody);
if (connection.getResponseCode() / 100 != 2) handleError(requestBody, connection);
if (hasAnswer) {
try (InputStream inputStream = connection.getInputStream()) {
final String bodyString = getBodyAsString(inputStream);
try {
return mapper.readValue(bodyString, answerClass);
} catch (Exception e) {
throw new GravityRecEngException(e.getMessage(), "");
}
}
} else {
return null;
}
}
private HttpURLConnection sendRequest(String methodName, Map queryStringParams, Object requestBody) throws IOException {
HttpURLConnection connection = createConnection(methodName, queryStringParams);
setRequestHeaders(connection);
if (userName == null) throw new IllegalStateException("set the user name");
if (password == null) throw new IllegalStateException("set the password");
String userPassword = userName + ":" + password;
connection.setRequestProperty("Authorization", "Basic " + new String(Base64.encodeBase64(userPassword.getBytes(UTF8)), UTF8));
sendPostRequest(requestBody, connection);
return connection;
}
private void handleError(Object requestBody, HttpURLConnection connection) throws IOException, GravityRecEngException {
final String responseBody = getBodyAsString(connection.getErrorStream());
final String b = requestBody == null ? "" : (requestBody.getClass().isArray() ? Arrays.toString((Object[]) requestBody) : requestBody.toString());
final String message = String.format("response code %d, for url %s | request content '%s' | answer '%s'", connection.getResponseCode(), connection.getURL(), b, responseBody);
if (responseBody == null) {
throw new GravityRecEngException(message, "");
} else {
try {
// noinspection UnnecessaryLocalVariable
GravityRecEngException exception = mapper.readValue(responseBody, GravityRecEngException.class);
throw exception;
} catch (GravityRecEngException e) {
throw e;
} catch (Exception e) {
throw new GravityRecEngException(message + " body " + responseBody, "");
}
}
}
private String getBodyAsString(InputStream input) throws IOException {
if (input == null) return null;
StringBuilder sb = new StringBuilder();
char[] buffer = new char[2048];
int readChars;
BufferedReader reader = new BufferedReader(new InputStreamReader(input, Charset.forName("UTF-8")));
while ((readChars = reader.read(buffer)) != -1) {
sb.append(buffer, 0, readChars);
}
return sb.toString();
}
private HttpURLConnection createConnection(String methodName, Map queryStringParams) throws IOException {
if (remoteUrl == null) throw new IllegalStateException("set the remote URL");
final String urlString = remoteUrl + "/" + methodName + getRequestQueryString(methodName, queryStringParams);
URL url = new URL(urlString);
return (HttpURLConnection) url.openConnection();
}
private void setRequestHeaders(HttpURLConnection connection) throws ProtocolException {
connection.setRequestMethod("POST");
connection.addRequestProperty("User-Agent", "Gravity-RecEng-JavaClient-Webshop");
connection.addRequestProperty("X-Gravity-RecEng-JavaClient-Webshop-Version", VERSION);
connection.setReadTimeout(readTimeout);
connection.setConnectTimeout(readTimeout);
connection.addRequestProperty("Content-Type", "application/json; charset=utf-8");
}
private void sendPostRequest(Object requestBody, HttpURLConnection connection) throws IOException {
connection.setDoOutput(true);
try (OutputStream outputStream = connection.getOutputStream()) {
try (final DataOutputStream wr = new DataOutputStream(outputStream)) {
final String requestJson = mapper.writeValueAsString(requestBody);
wr.writeBytes(requestJson);
wr.flush();
}
}
}
private String getRequestQueryString(String methodName, Map queryStringParams) throws UnsupportedEncodingException {
StringBuilder queryString = new StringBuilder();
if (queryStringParams != null) {
for (Entry pair : queryStringParams.entrySet()) {
queryString.append(URLEncoder.encode(pair.getKey(), "UTF-8")).append("=");
queryString.append(URLEncoder.encode(pair.getValue(), "UTF-8")).append("&");
}
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
queryString.insert(0, "&");
}
return "?method=" + URLEncoder.encode(methodName, "UTF-8") + queryString.toString();
}
public String getRemoteUrl() {
return remoteUrl;
}
/**
* Set the URL of the server side interface. It has no default value, must be specified.
* @param remoteUrl the server url provided by Gravity integration team
*/
public void setRemoteUrl(String remoteUrl) {
this.remoteUrl = remoteUrl;
}
public String getUserName() {
return userName;
}
/**
* Set the user name for the http authenticated connection. Leave it blank in case of
* connection without authentication.
* @param userName user authentication name provided by Gravity
*/
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
/**
* Set the password for the http authenticated connection. Leave it blank in case of
* connection without authentication.
* @param password user authentication password provided by Gravity
*/
public void setPassword(String password) {
this.password = password;
}
public int getReadTimeout() {
return readTimeout;
}
/**
* Set the timeout for the operations in millisecs. The default value is 3000 millisecs.
* @param readTimeout wait up to this millisecond for the request answers
*/
public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
}
/**
* Adds events to the recommendation engine.
*
* @param events The events to add.
* @param async true if the call is asynchronous. An asynchronous call
* returns immediately after an input data checking,
* a synchronous call returns only after the data is saved to database.
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer files
*/
public void addEvents(GravityEvent[] events, boolean async) throws GravityRecEngException, IOException {
HashMap queryStringParams = new HashMap<>();
queryStringParams.put("async", Boolean.toString(async));
sendRequest("addEvents", queryStringParams, events, false, null);
}
/**
* Adds users to the recommendation engine. The existing users will be updated.
* If a user already exists with the specified userId,
* the entire user will be replaced with the new user specified here.
*
* @param users The user to add.
* @param async true if the call is asynchronous. An asynchronous call
* returns immediately after an input data checking,
* a synchronous call returns only after the data is saved to database.
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer files
*/
public void addUsers(GravityUser[] users, boolean async) throws GravityRecEngException, IOException {
HashMap queryStringParams = new HashMap<>();
queryStringParams.put("async", Boolean.toString(async));
sendRequest("addUsers", queryStringParams, users, false, null);
}
/**
* Retrieves user metadata from the recommendation engine.
*
* @param userId {@link GravityUser#userId}
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer files
*/
public GravityUser getUserByUserId(String userId) throws GravityRecEngException, IOException {
HashMap queryStringParams = new HashMap<>(1);
queryStringParams.put("userId", userId);
return (GravityUser) sendRequest("getUser", queryStringParams, null, true, GravityUser.class);
}
/**
* Retrieves user metadata from the recommendation engine if a user can be recognized from the specified cookieId.
*
* @param cookieId {@link GravityEvent#cookieId}
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer files
*/
public GravityUser getUserByCookieId(String cookieId) throws GravityRecEngException, IOException {
HashMap queryStringParams = new HashMap<>(1);
queryStringParams.put("cookieId", cookieId);
return (GravityUser) sendRequest("getUser", queryStringParams, null, true, GravityUser.class);
}
/**
* Retrieves full event history associated with the userId from the recommendation engine.
*
* @param userId {@link GravityEvent#userId}
* @param limit upper limit for returned events. If 0 or negative a default limit will be used
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer files
*/
public GravityEvent[] getEventsByUserId(String userId, int limit) throws GravityRecEngException, IOException {
HashMap queryStringParams = new HashMap<>(1);
queryStringParams.put("userId", userId);
if (limit > 0) queryStringParams.put("limit", String.valueOf(limit));
return (GravityEvent[]) sendRequest("getEvents", queryStringParams, null, true, GravityEvent[].class);
}
/**
* Retrieves full event history associated with the cookieId from the recommendation engine.
*
* @param cookieId {@link GravityEvent#cookieId}
* @param limit upper limit for returned events. If 0 or negative a default limit will be used
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer files
*/
public GravityEvent[] getEventsByCookieId(String cookieId, int limit) throws GravityRecEngException, IOException {
HashMap queryStringParams = new HashMap<>(1);
queryStringParams.put("cookieId", cookieId);
if (limit > 0) queryStringParams.put("limit", String.valueOf(limit));
return (GravityEvent[]) sendRequest("getEvents", queryStringParams, null, true, GravityEvent[].class);
}
/**
* Adds items to the recommendation engine.
* If an item already exists with the specified itemId,
* the entire item along with its NameValue pairs will be replaced to the new item specified here.
*
* @param items The items to add
* @param async true if the call is asynchronous. An asynchronous call
* returns immediately after an input data checking,
* a synchronous call returns only after the data is saved to
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer filesdatabase.
*/
public void addItems(GravityItem[] items, boolean async) throws GravityRecEngException, IOException {
HashMap queryStringParams = new HashMap<>();
queryStringParams.put("async", Boolean.toString(async));
sendRequest("addItems", queryStringParams, items, false, null);
}
/**
* Returns a list of recommended items, based on the given context parameters.
*
* @param userId The identifier of the logged in user. If no user is logged in, null should be specified.
* @param cookieId It should be a permanent identifier for the end users computer, preserving its value across browser sessions.
* It should be always specified.
* @param context Additional information which describes the actual scenario.
* @return An object containing the recommended items and other information about the recommendation.
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer files
*/
public GravityItemRecommendation getItemRecommendation(String userId, String cookieId, GravityRecommendationContext context) throws GravityRecEngException, IOException {
HashMap queryStringParams = new HashMap<>();
if (userId != null) {
queryStringParams.put("userId", userId);
}
if (cookieId != null) {
queryStringParams.put("cookieId", cookieId);
}
return (GravityItemRecommendation) sendRequest("getItemRecommendation",
queryStringParams, context, true, GravityItemRecommendation.class);
}
/**
* Given the userId and the cookieId, we can request recommendations for multiple scenarios (described by the context).
* This function returns lists of recommended items for each of the given scenarios in an array.
*
* @param userId The identifier of the logged in user. If no user is logged in, null should be specified.
* @param cookieId It should be a permanent identifier for the end users computer, preserving its value across browser sessions.
* It should be always specified.
* @param context Additional Array of information which describes the actual scenarios.
* @return An Array containing the recommended items for each scenario with other information about the recommendation.
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer files
*/
public GravityItemRecommendation[] getItemRecommendationBulk(String userId, String cookieId, GravityRecommendationContext[] context) throws GravityRecEngException, IOException {
HashMap queryStringParams = new HashMap<>();
if (userId != null) {
queryStringParams.put("userId", userId);
}
if (cookieId != null) {
queryStringParams.put("cookieId", cookieId);
}
return (GravityItemRecommendation[]) sendRequest("getItemRecommendationBulk",
queryStringParams, context, true, GravityItemRecommendation[].class);
}
/**
* Simple test function to test without side effects whether the service is alive.
* @param name a test string
* @return "Hello " + name
* @throws IOException if cannot connect
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer files
*/
public String test(String name) throws GravityRecEngException, IOException {
HashMap queryStringParams = new HashMap<>();
queryStringParams.put("name", name);
return (String) sendRequest("test", queryStringParams, name, true, String.class);
}
/**
* Simple test function to test throwing an exception.
* @throws IOException if cannot connect
* @throws GravityRecEngException if cannot process the answer files
*/
public void testException() throws GravityRecEngException, IOException {
sendRequest("testException", null, null, true, null);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy