Merge branch 'master' of https://github.com/PredictionIO/PredictionIO-Java-SDK
diff --git a/README.md b/README.md
index 88d0d81..8fadd34 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
<dependency>
<groupId>io.prediction</groupId>
<artifactId>client</artifactId>
- <version>0.8.3</version>
+ <version>0.9.5</version>
</dependency>
</dependencies>
...
@@ -36,7 +36,7 @@
<ivy-module ...>
...
<dependencies>
- <dependency org="io.prediction" name="client" rev="0.8.3" />
+ <dependency org="io.prediction" name="client" rev="0.9.5" />
...
</dependencies>
...
@@ -49,7 +49,7 @@
If you have an sbt project, add the library dependency to your build definition.
```Scala
-libraryDependencies += "io.prediction" % "client" % "0.8.3"
+libraryDependencies += "io.prediction" % "client" % "0.9.5"
```
@@ -95,7 +95,7 @@
<dependency>
<groupId>io.prediction</groupId>
<artifactId>client</artifactId>
- <version>0.8.4-SNAPSHOT</version>
+ <version>0.9.6-SNAPSHOT</version>
</dependency>
</dependencies>
...
diff --git a/client/pom.xml b/client/pom.xml
index d0b2b41..e844bfb 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -5,7 +5,7 @@
<groupId>io.prediction</groupId>
<artifactId>sdk</artifactId>
<relativePath>../pom.xml</relativePath>
- <version>0.8.3</version>
+ <version>0.9.6-SNAPSHOT</version>
</parent>
<artifactId>client</artifactId>
diff --git a/client/src/main/java/io/prediction/EventClient.java b/client/src/main/java/io/prediction/EventClient.java
index 8086328..1f42926 100644
--- a/client/src/main/java/io/prediction/EventClient.java
+++ b/client/src/main/java/io/prediction/EventClient.java
@@ -5,6 +5,8 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonArray;
import com.ning.http.client.Request;
import com.ning.http.client.RequestBuilder;
@@ -12,6 +14,7 @@
import java.io.IOException;
import java.util.List;
+import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@@ -151,6 +154,68 @@
}
/**
+ * Sends an asynchronous create events (batch) request to the API.
+ *
+ * @param events a List of {@link Event} that will be turned into a request
+ */
+ public FutureAPIResponse createEventsAsFuture(List<Event> events) throws IOException {
+ RequestBuilder builder = new RequestBuilder("POST");
+ builder.setUrl(apiUrl + "/batch/events.json?accessKey=" + accessKey);
+
+ GsonBuilder gsonBuilder = new GsonBuilder();
+ gsonBuilder.registerTypeAdapter(DateTime.class, new DateTimeAdapter());
+ Gson gson = gsonBuilder.create();
+ String requestJsonString = gson.toJson(events);
+
+ builder.setBody(requestJsonString);
+ builder.setHeader("Content-Type","application/json");
+ builder.setHeader("Content-Length", ""+requestJsonString.length());
+ return new FutureAPIResponse(client.executeRequest(builder.build(), getHandler()));
+ }
+
+ /**
+ * Sends a synchronous create events (batch) request to the API.
+ *
+ * @param events a List of {@link Event} that will be turned into a request
+ * @return event ID from the server
+ *
+ * @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 List<String> createEvents(List<Event> event)
+ throws ExecutionException, InterruptedException, IOException {
+ return createEvents(createEventsAsFuture(event));
+ }
+
+ /**
+ * Synchronize a previously sent asynchronous create events (batch) request.
+ *
+ * @param response an instance of {@link FutureAPIResponse} returned from
+ * {@link #createEventAsFuture}
+ * @return List of event IDs from the server
+ *
+ * @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 List<String> createEvents(FutureAPIResponse response)
+ throws ExecutionException, InterruptedException, IOException {
+ int status = response.get().getStatus();
+ String message = response.get().getMessage();
+
+ if (status != BaseClient.HTTP_OK) {
+ throw new IOException(status + " " + message);
+ }
+ List<String> eventIds = new LinkedList<String>();
+
+ for(JsonElement elem: (JsonArray)parser.parse(message) ){
+ eventIds.add(((JsonObject)elem).get("eventId").getAsString());
+ }
+ return eventIds;
+ }
+
+ /**
* Sends an asynchronous get event request to the API.
*
* @param eid ID of the event to get
diff --git a/client/src/main/java/io/prediction/FileExporter.java b/client/src/main/java/io/prediction/FileExporter.java
new file mode 100644
index 0000000..7919ee9
--- /dev/null
+++ b/client/src/main/java/io/prediction/FileExporter.java
@@ -0,0 +1,64 @@
+package io.prediction;
+
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import org.joda.time.DateTime;
+
+public class FileExporter {
+
+ private FileOutputStream out;
+
+ public FileExporter(String pathname) throws FileNotFoundException {
+ out = new FileOutputStream(pathname);
+ }
+
+ /**
+ * Create and write a json-encoded event to the underlying file.
+ *
+ * @param eventName Name of the event.
+ * @param entityType The entity type.
+ * @param entityId The entity ID.
+ * @param targetEntityType The target entity type (optional).
+ * @param targetEntityId The target entity ID (optional).
+ * @param properties Properties (optional).
+ * @param eventTime The time of the event (optional).
+ * @throws IOException
+ */
+ public void createEvent(String eventName, String entityType, String entityId,
+ String targetEntityType, String targetEntityId, Map<String, Object> properties,
+ DateTime eventTime) throws IOException {
+
+ if (eventTime == null) {
+ eventTime = new DateTime();
+ }
+
+ Event event = new Event()
+ .event(eventName)
+ .entityType(entityType)
+ .entityId(entityId)
+ .eventTime(eventTime);
+
+ if (targetEntityType != null) {
+ event.targetEntityType(targetEntityType);
+ }
+
+ if (targetEntityId != null) {
+ event.targetEntityId(targetEntityId);
+ }
+
+ if (properties != null) {
+ event.properties(properties);
+ }
+
+ out.write(event.toJsonString().getBytes("UTF8"));
+ out.write('\n');
+ }
+
+ public void close() throws IOException {
+ out.close();
+ }
+
+}
diff --git a/client/src/test/java/io/prediction/FileExporterTest.java b/client/src/test/java/io/prediction/FileExporterTest.java
new file mode 100644
index 0000000..a5c7759
--- /dev/null
+++ b/client/src/test/java/io/prediction/FileExporterTest.java
@@ -0,0 +1,116 @@
+package io.prediction;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.joda.time.DateTime;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class FileExporterTest {
+
+ @Rule
+ public TemporaryFolder folder = new TemporaryFolder();
+
+ @Test
+ public void testIt() throws IOException {
+
+ String pathname = folder.getRoot().getCanonicalPath() + "/testIt.out";
+
+ FileExporter exporter = new FileExporter(pathname);
+
+ Map<String, Object> properties = new HashMap<>();
+ properties.put("birthday", new DateTime("1758-05-06T00:00:00+00:00"));
+
+ DateTime then = new DateTime("1794-07-27T00:00:00+00:00");
+
+ exporter.createEvent("event-1", "entity-type-1", "entity-id-1",
+ null, null, null, null);
+
+ exporter.createEvent("event-2", "entity-type-2", "entity-id-2",
+ "target-entity-type-2", null, null, null);
+
+ exporter.createEvent("event-3", "entity-type-3", "entity-id-3",
+ null, "target-entity-id-3", null, null);
+
+ exporter.createEvent("event-4", "entity-type-4", "entity-id-4",
+ null, null, properties, then);
+
+ exporter.createEvent("event-5", "entity-type-5", "entity-id-5",
+ "target-entity-type-5", "target-entity-id-5", properties, then);
+
+ exporter.close();
+
+ File out = new File(pathname);
+ assertTrue(pathname + " exists", out.exists());
+
+ BufferedReader reader = new BufferedReader(new FileReader(pathname));
+
+ GsonBuilder gsonBuilder = new GsonBuilder();
+ gsonBuilder.registerTypeAdapter(DateTime.class, new DateTimeAdapter());
+ Gson gson = gsonBuilder.create();
+
+ String json1 = reader.readLine();
+ Event event1 = gson.fromJson(json1, Event.class);
+ assertEquals("event-1", event1.getEvent());
+ assertEquals("entity-type-1", event1.getEntityType());
+ assertEquals("entity-id-1", event1.getEntityId());
+ assertNull(event1.getTargetEntityType());
+ assertNull(event1.getTargetEntityId());
+ assertTrue(event1.getProperties().isEmpty());
+ assertEquals(new DateTime().getMillis(), event1.getEventTime().getMillis(), 1000);
+
+ String json2 = reader.readLine();
+ Event event2 = gson.fromJson(json2, Event.class);
+ assertEquals("event-2", event2.getEvent());
+ assertEquals("entity-type-2", event2.getEntityType());
+ assertEquals("entity-id-2", event2.getEntityId());
+ assertEquals("target-entity-type-2", event2.getTargetEntityType());
+ assertNull(event2.getTargetEntityId());
+ assertTrue(event2.getProperties().isEmpty());
+ assertEquals(new DateTime().getMillis(), event2.getEventTime().getMillis(), 1000);
+
+ String json3 = reader.readLine();
+ Event event3 = gson.fromJson(json3, Event.class);
+ assertEquals("event-3", event3.getEvent());
+ assertEquals("entity-type-3", event3.getEntityType());
+ assertEquals("entity-id-3", event3.getEntityId());
+ assertNull(event3.getTargetEntityType());
+ assertEquals("target-entity-id-3", event3.getTargetEntityId());
+ assertTrue(event3.getProperties().isEmpty());
+ assertEquals(new DateTime().getMillis(), event3.getEventTime().getMillis(), 1000);
+
+ String json4 = reader.readLine();
+ Event event4 = gson.fromJson(json4, Event.class);
+ assertEquals("event-4", event4.getEvent());
+ assertEquals("entity-type-4", event4.getEntityType());
+ assertEquals("entity-id-4", event4.getEntityId());
+ assertNull(event4.getTargetEntityType());
+ assertNull(event4.getTargetEntityId());
+ assertEquals(1, event4.getProperties().size());
+ assertEquals(properties.get("birthday"), new DateTime(event4.getProperties().get("birthday")));
+ assertEquals(then.getMillis(), event4.getEventTime().getMillis());
+
+ String json5 = reader.readLine();
+ Event event5 = gson.fromJson(json5, Event.class);
+ assertEquals("event-5", event5.getEvent());
+ assertEquals("entity-type-5", event5.getEntityType());
+ assertEquals("entity-id-5", event5.getEntityId());
+ assertEquals("target-entity-type-5", event5.getTargetEntityType());
+ assertEquals("target-entity-id-5", event5.getTargetEntityId());
+ assertEquals(1, event5.getProperties().size());
+ assertEquals(properties.get("birthday"), new DateTime(event5.getProperties().get("birthday")));
+ assertEquals(then.getMillis(), event4.getEventTime().getMillis());
+
+ String empty = reader.readLine();
+ assertNull("no more data", empty);
+ }
+}
diff --git a/examples/import/pom.xml b/examples/import/pom.xml
index 9f6bd49..fef7ec1 100644
--- a/examples/import/pom.xml
+++ b/examples/import/pom.xml
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.prediction.samples</groupId>
<artifactId>sample-import</artifactId>
- <version>0.8.3</version>
+ <version>0.9.5</version>
<packaging>jar</packaging>
<name>PredictionIO Java SDK Examples: Import</name>
@@ -11,7 +11,7 @@
<dependency>
<groupId>io.prediction</groupId>
<artifactId>client</artifactId>
- <version>0.8.3</version>
+ <version>0.9.5</version>
</dependency>
</dependencies>
diff --git a/examples/quickstart_import/pom.xml b/examples/quickstart_import/pom.xml
index e7f0755..67595f6 100644
--- a/examples/quickstart_import/pom.xml
+++ b/examples/quickstart_import/pom.xml
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.prediction.samples</groupId>
<artifactId>quickstart-import</artifactId>
- <version>0.8.3</version>
+ <version>0.9.5</version>
<packaging>jar</packaging>
<name>PredictionIO Java SDK Examples: Quickstart Import</name>
@@ -11,7 +11,7 @@
<dependency>
<groupId>io.prediction</groupId>
<artifactId>client</artifactId>
- <version>0.8.3</version>
+ <version>0.9.5</version>
</dependency>
</dependencies>
diff --git a/examples/quickstart_import/src/main/java/io/prediction/samples/QuickstartImport.java b/examples/quickstart_import/src/main/java/io/prediction/samples/QuickstartImport.java
index c82fb3f..257e0ae 100644
--- a/examples/quickstart_import/src/main/java/io/prediction/samples/QuickstartImport.java
+++ b/examples/quickstart_import/src/main/java/io/prediction/samples/QuickstartImport.java
@@ -9,6 +9,12 @@
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutionException;
+import java.util.List;
+import java.util.LinkedList;
+import org.joda.time.DateTime;
+
+import io.prediction.Event;
+
public class QuickstartImport {
public static void main(String[] args)
@@ -45,6 +51,27 @@
}
}
+ List<Event> events = new LinkedList<Event>();
+
+ // Use only 5 users because max batch size is 50
+ // Throws IOException w/ details inside if this is exceeded
+ for (int user = 1; user <= 5; user++) {
+ for (int i = 1; i <= 10; i++) {
+ int item = rand.nextInt(50) + 1;
+ System.out.println("User " + user + " views item " + item);
+ events.add(new Event()
+ .event("view")
+ .entityType("user")
+ .entityId(""+user)
+ .targetEntityType("item")
+ .targetEntityId(""+item)
+ .properties(emptyProperty)
+ .eventTime(new DateTime()));
+ }
+ }
+
+ client.createEvents(events);
+
client.close();
}
}
diff --git a/pom.xml b/pom.xml
index b4693b6..1500ed8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.prediction</groupId>
<artifactId>sdk</artifactId>
- <version>0.8.3</version>
+ <version>0.9.6-SNAPSHOT</version>
<url>http://prediction.io</url>
<packaging>pom</packaging>
<name>PredictionIO Java SDK</name>