Snowplow Java Tracker 0.5.0 released

18 August 2014  •  Jonathan Almeida

We’re excited to announce another release of the Snowplow Java Tracker version 0.5.0

This release comes with a few changes to the Tracker method signatures to support our upcoming Snowplow 0.9.7 release with POST support, bug fixes, and more. Notably, we’ve added a new class for supporting your context data.

I’ll be covering everything mentioned above in more detail:

  1. Project structure changes
  2. Collector endpoint changes for POST requests
  3. The SchemaPayload Class
  4. Emitter callback
  5. Configuring the buffer
  6. Tracker context bug fix
  7. Miscellaneouss
  8. Support

1. Project structure changes

We have changed the project structure so that the Java Tracker is now java-tracker-core as a subproject of the root snowplow-java-tracker project. The structure looks something like this:

snowplow-java-tracker/
|_ build.gradle
|_ ...
|_ java-tracker-core/
   |_ build.gradle
   |_ ...

This is part of a re-structuring to make space for a java-tracker-server that we’re looking to add in the future, and to allow code re-use with the Snowplow Android Tracker, which is coming soon. What this means for you, is that some enum classes have been moved from the com.snowplowanalytics.snowplow.tracker package to com.snowplowanalytics.snowplow.tracker.core. If you’re pulling the tracker straight from GitHub and you come across any caching warnings, try removing your current Tracker project and do a clean pull.

2. Collector endpoint changes for POST requests

We decided to make a change to the collector endpoint for POST requests, so that the URI path would follow the format /[api_vendor]/[api_version]. This is similar to how we append /i to the collector endpoint. So an example of what the URI would look would be:

http://collector.acme.net/com.snowplowanalytics.snowplow/tp2

If requests are being sent as GET, we default to appending the original /i to the end of the collector URI.

2. The SchemaPayload Class

A new class SchemaPayload has been added as a wrapper around your custom contexts. The idea is to make sure that each context is a valid self-describing JSON which Snowplow can process. Hence, a SchemaPayload instance provides two methods setSchema and setData. Here’s an example if your context was a simple map:

// Let's say your context is a simple map
Map<String, String> contextMap = new HashMap<String, String>();
contextMap.put("someContextKey", "someContextValue");

// Create a SchemaPayload to wrap that context
SchemaPayload schemaPayload = new SchemaPayload();
// Set the schema you that describes your context
schemaPayload.setSchema("iglu:com.snowplowanalytics.snowplow/my_schema/jsonschema/1-0-0");
// Set the context as the data
schemaPayload.setData(contextMap);

// All contexts need to be passed in as a list so we add it to one
ArrayList<SchemaPayload> contextList = new ArrayList<SchemaPayload>();
contextList.add(schemaPayload);

// For completeness, let's add this context to a page view without Base64 encoding
tracker.trackPageView("www.mypage.com", "My Page", "www.me.com", contextList);

What this ends up looking like in a JSON format, note the co property:

{
  "schema": "iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-0",
  "data": [
    {
      "e": "pv",
      "url": "www.mypage.com",
      "page": "My Page",
      "refr": "www.me.com",
      "aid": "cloudfront",
      "tna": "AF003",
      "tv": "java-0.5.0",
      "dtm": "1407831713559",
      "co": "{\"schema\":\"iglu:com.snowplowanalytics.snowplow/contexts/jsonschema/1-0-0\",\"data\":[{\"schema\":\"setse\",\"data\":{\"someContextKey\":\"someContextValue\"}}]}",
      "tz": "America/Toronto",
      "p": "pc",
      "vp": "320x480",
      "eid": "c7526a7f-a545-431e-a224-277c791111c1"
    }
  ]
}

The new class also changes the track methods from accepting a context of List<Map> to List<SchemaPayload>. Here’s an example of the new method signatures:

// Previous
trackPageView(String pageUrl, String pageTitle, String referrer, List<Map> context, double timestamp)
// Now
trackPageView(String pageUrl, String pageTitle, String referrer, List<SchemaPayload> context, double timestamp)

4. Emitter callback

The Emitter class now supports callbacks for success/failure of sending events. If events fail to send, you can now choose how to handle that failure, by passing in a class using the RequestCallback interface to the Emitter object. Here’s an example to make it easier to understand:

Emitter emitter = new Emitter(testURL, HttpMethod.GET, new RequestCallback() {
  @Override
  public void onSuccess(int successCount) {
    System.out.println("Success count for POST/GET:" + successCount);
  }

  @Override
  public void onFailure(int successCount, List<Payload> failedEvent) {
    System.out.println("Failure, successCount: " + successCount + "\nfailedEvent:\n" + failedEvent.toString());
  }
});

If events are all successfully sent, the onSuccess method returns the number of successful events sent. If there were any failures, the onFailure method returns the successful events sent (if any) and a list of events that failed to be sent (i.e. the HTTP state code did not return 200).

We’ve also added two new Emitter constructors to support callbacks:

Emitter(String URI, RequestCallback callback)
Emitter(String URI, HttpMethod httpMethod, RequestCallback callback)

This is an optional feature, so if you choose to not worry about the failed events, you can still use the original Emitter constructors:

Emitter(String URI)
Emitter(String URI, HttpMethod httpMethod)

5. Configuring the buffer

We’ve changed the default behavior of sending events in this update. When you create an Emitter and set the HttpMethod to send GET requests, we default the Emitter to send events instantly upon being tracked. It makes most sense to send GET requests immediately since they cannot be grouped like events sent via POST.

Here is a short example:

// By default BufferOption.Instant is set for GET
Emitter emitter = new Emitter("collector.acme.net", HttpMethod.GET);

// By default BufferOption.Default is set as the buffer option for POST...
Emitter emitter = new Emitter("collector.acme.net", HttpMethod.POST);

// ... but we can still change that if we like
emitter.setBufferOption(BufferOption.Instant);

6. Tracker context bug fix

There was a bug in our tracking method signatures whereby the context argument was passed as a Map. We have now fixed this: all signatures expect a List of contexts, using the new SchemaPayload as mentioned above. The new type for passing the context is List<SchemaPayload>.

7. Miscellaneous

We have made a few miscellaneous fixes in this version, including:

  • We have added some unit tests for the Subject class
  • Base64 encoding of unstructured event and context JSONs now uses UTF-8 not US-ASCII

7. Support

Please get in touch if you need help setting up the Snowplow Java Tracker or want to suggest a new feature. And of course if you find any bugs, please do raise an issue.

For more details on this release, please check out the 0.5.0 Release Notes on GitHub.