Java tutorial
/* * Copyright (c) 2012-2014 Snowplow Analytics Ltd. All rights reserved. * * This program is licensed to you under the Apache License Version 2.0, * and you may not use this file except in compliance with the Apache License Version 2.0. * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, * software distributed under the Apache License Version 2.0 is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ package com.saggezza.jtracker.track; import com.saggezza.jtracker.emit.Emitter; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.net.URISyntaxException; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * <p>TrackerC is the implementing class for the Tracker interface.</p> * <p>It can be used to track many different types of events. The current version * of the tracker is 0.1.0 meaning it is functional but likely contains some bugs. * If you find any please contact me at kaagleason@gmail.com or contact com.saggezza * via their website.</p> * * <p><b>To use:</b></p> * <ul> * <li>Import the com.com.saggezza.com.saggezza.jtracker package</li> * <li>You must first declare a payload and a tracker</li> * <li>Tracker will instantiate with a good-for-tracking payload</li> * <li>(recommended) Build the payload identity with TrackerC set functions (e.g setUser(String user))</li> * <li>(optional) Add custom payload to Tracker with setPayload(). <br/>Payload will configure itself further and prepare for request.</li> * <li>Call a specific tracking event. Payload will be automatically configured further</li> * <li>Get request is sent to server</li> * </ul> * * <p><b>There are two static functions in the TrackerC class: "debug" and "track"</b></p> * <ul> * <li>To see the output you are sending to the web as it is sending, put `TrackerC.debug = true` in your code.</li> * <li>If you would like to test what the output would look like in your usage scenario without<br/> * sending logs to your S3 bucket, put `TrackerC.track = false` in your code.</li> * <li>Default values are `TrackerC.debug = false` and `TrackerC.track = true`</li> * </ul> * @version 0.2.0 * @author Kevin Gleason */ public class TrackerC implements Tracker, GenericTracker { //Static Class variables private static final String VERSION = Version.VERSION; private static final String DEFAULT_PLATFORM = "pc"; public static final String DEFAULT_VENDOR = "com.com.saggezza"; //Static public static boolean debug = false; public static boolean track = true; public static boolean singleVar = false; //Instance Variables private PayloadMap payload = new PayloadMapC(); private PlowContractor<String> stringContractor = new PlowContractor<String>(); private PlowContractor<Integer> integerContractor = new PlowContractor<Integer>(); private String namespace, app_id, context_vendor; private boolean base64_encode, contracts; private Emitter emitter; private ExecutorService executor = Executors.newCachedThreadPool(); //Base Constructor public TrackerC(Emitter emitter, String namespace) { this.emitter = emitter; this.namespace = namespace; this.app_id = this.context_vendor = ""; this.base64_encode = this.contracts = true; this.setPayload(new PayloadMapC()); } //Constructor with all arguments public TrackerC(Emitter emitter, String namespace, String app_id, String context_vendor, boolean base64_encode, boolean contracts) { this.emitter = emitter; this.namespace = namespace; this.app_id = app_id; this.context_vendor = context_vendor; this.base64_encode = base64_encode; this.contracts = contracts; this.setPayload(new PayloadMapC()); } /** * {@inheritDoc} * @param user_id The current user of the JavaPlow tracker as used in analytics. */ public void setupTrack(String user_id, JSONObject metaData) { this.payload = this.payload.addSystemInfo(); this.setUserID(user_id); } /** * {@inheritDoc} * @throws java.io.IOException */ public void track() { this.payload = this.payload.setTimestamp(); if (TrackerC.debug) { System.out.println("Payload:\n" + payload.toString()); System.out.println("Making HttpGet..."); } this.executor.execute(new Emitter(this.emitter, this.payload)); // this.emitter.emit(this.payload); this.clearPayload(); } /** * {@inheritDoc} * @param page_url The url of the page where the tracking call lies. * @param page_title The title of the page where the tracking call lies. (optional) * @param referrer The one who referred you to the page (optional) * @param context Additional JSON context for the tracking call (optional) * @throws org.json.JSONException * @throws java.io.IOException */ public void trackPageView(String page_url, String page_title, String referrer, String context) throws JSONException, IOException { assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, page_url); if (context != null && !context.equals("")) { JSONObject jsonContext = stringToJSON(context); this.payload = this.payload.trackPageView_config(page_url, page_title, referrer, jsonContext); } else { this.payload = this.payload.trackPageView_config(page_url, page_title, referrer, null); } this.track(); } /** * {@inheritDoc} * @param category The category of the structured event. * @param action The action that is being tracked. (optional) * @param label A label for the tracking event. (optional) * @param property The property of the structured event being tracked. (optional) * @param value The value associated with the property being tracked. * @param vendor The vendor the the property being tracked. (optional) * @param context Additional JSON context for the tracking call (optional) * @throws org.json.JSONException * @throws java.io.IOException */ public void trackStructEvent(String category, String action, String label, String property, int value, String vendor, String context) throws JSONException, IOException { String valueStr = String.valueOf(value); assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, category); assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, valueStr); if (context != null && !context.equals("")) { JSONObject jsonContext = stringToJSON(context); this.payload = this.payload.trackStructEvent_config(category, action, label, property, valueStr, jsonContext); } else { this.payload = this.payload.trackStructEvent_config(category, action, label, property, valueStr, null); } this.track(); } /** * {@inheritDoc} * @param eventVendor The vendor the the event information. * @param eventName A name for the unstructured event being tracked. * @param dictInfo The unstructured information being tracked in dictionary form. * @param context Additional JSON context for the tracking call (optional) * @throws org.json.JSONException * @throws java.io.IOException */ public void trackUnstructEvent(String eventVendor, String eventName, Map<String, Object> dictInfo, String context) throws JSONException, IOException { assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, eventVendor); assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, eventName); assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyDict, dictInfo.toString()); JSONObject jsonDict = mapToJSON(dictInfo); //Make compatible for Map<String, Object> if (context != null && !context.equals("")) { JSONObject jsonContext = stringToJSON(context); this.payload = this.payload.trackUnstructEvent_config(eventVendor, eventName, jsonDict, jsonContext); } else { this.payload = this.payload.trackUnstructEvent_config(eventVendor, eventName, jsonDict, null); } this.track(); } /** * {@inheritDoc} * @param eventVendor The vendor the the event information. * @param eventName A name for the unstructured event being tracked. * @param dictInfo The unstructured information being tracked in dictionary form. * @param context Additional JSON context for the tracking call (optional) * @throws org.json.JSONException * @throws java.io.IOException */ public void trackUnstructEvent(String eventVendor, String eventName, String dictInfo, String context) throws JSONException, IOException { assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, eventVendor); assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, eventName); assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyDict, dictInfo.toString()); JSONObject jsonDict = stringToJSON(dictInfo); //Make compatible for Map<String, Object> if (context != null && !context.equals("")) { JSONObject jsonContext = stringToJSON(context); this.payload = this.payload.trackUnstructEvent_config(eventVendor, eventName, jsonDict, jsonContext); } else { this.payload = this.payload.trackUnstructEvent_config(eventVendor, eventName, jsonDict, null); } this.track(); } /** * {@inheritDoc} * @param eventVendor The vendor the the event information. * @param eventName A name for the unstructured event being tracked. * @param dictInfo The unstructured information being tracked in dictionary form. * @param context Additional JSON context for the tracking call (optional) * @throws org.json.JSONException * @throws java.io.IOException */ public void trackGenericEvent(String eventVendor, String eventName, Map<String, Object> dictInfo, String context) throws JSONException, IOException { assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, eventVendor); assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, eventName); assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyDict, dictInfo.toString()); if (context != null && !context.equals("")) { JSONObject jsonContext = stringToJSON(context); this.payload = this.payload.trackGenericEvent_config(eventVendor, eventName, dictInfo, jsonContext); } else { this.payload = this.payload.trackGenericEvent_config(eventVendor, eventName, dictInfo, null); } this.track(); } /** * {@inheritDoc} * @param name The name of the screen view being tracked * @param id The ID of the screen view being tracked. * @param context Additional JSON context for the tracking call (optional) * @throws org.json.JSONException * @throws java.io.IOException */ public void trackScreenView(String name, String id, String context) throws JSONException, IOException { assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, name); Map<String, Object> screenViewProperties = new LinkedHashMap<String, Object>(); screenViewProperties.put("Name", name); // or String screenVie... = "{'name': '"+ name + "'}" if (id != null) this.payload.add("id", id); this.trackUnstructEvent(DEFAULT_VENDOR, "screen_view", screenViewProperties, context); } /** * {@inheritDoc} * @param order_id ID of the item. * @param sku SKU value of the item. * @param price Prive of the item. * @param quantity Quantity of the item. * @param name Name of the item. * @param category Category of the item. * @param currency Currency used for the purchase. * @param context Additional JSON context for the tracking call (optional) * @param transaction_id Transaction ID, if left blank new value will be generated. * @throws org.json.JSONException * @throws java.io.IOException */ public void trackEcommerceTransactionItem(String order_id, String sku, Double price, Integer quantity, String name, String category, String currency, String context, String transaction_id) throws JSONException, IOException { assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, order_id); assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, sku); if (context != null && !context.equals("")) { JSONObject jsonContext = stringToJSON(context); this.payload = this.payload.trackEcommerceTransactionItem_config(order_id, sku, doubleCheck(price), integerCheck(quantity), stringCheck(name), stringCheck(category), stringCheck(currency), jsonContext, null); } else { this.payload = this.payload.trackEcommerceTransactionItem_config(order_id, sku, doubleCheck(price), integerCheck(quantity), stringCheck(name), stringCheck(category), stringCheck(currency), null, null); } this.track(); } /** * {@inheritDoc} * @param order_id The transaction ID, will be generated if left null * @param total_value The total value of the transaction. * @param affiliation Affiliations to the transaction (optional) * @param tax_value Tax value of the transaction (optional) * @param shipping Shipping costs of the transaction (optional) * @param city The customers city. * @param state The customers state. * @param country The customers country. * @param currency The currency used for the purchase * @param items A list containing a Map of Strings. Each item must have order ID, sku, price, and quantity. * @param context Additional JSON context for the tracking call (optional) * @throws org.json.JSONException * @throws java.io.UnsupportedEncodingException * @throws java.io.IOException */ public void trackEcommerceTransaction(String order_id, Double total_value, String affiliation, Double tax_value, Double shipping, String city, String state, String country, String currency, List<Map<String, String>> items, String context) throws JSONException, IOException { assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, order_id); //Track ecommerce event. if (context != null && !context.equals("")) { JSONObject jsonContext = stringToJSON(context); this.payload = this.payload.trackEcommerceTransaction_config(order_id, doubleCheck(total_value), stringCheck(affiliation), doubleCheck(tax_value), doubleCheck(shipping), stringCheck(city), stringCheck(state), stringCheck(country), stringCheck(currency), jsonContext); } else { this.payload = this.payload.trackEcommerceTransaction_config(order_id, doubleCheck(total_value), stringCheck(affiliation), doubleCheck(tax_value), doubleCheck(shipping), stringCheck(city), stringCheck(state), stringCheck(country), stringCheck(currency), null); } this.track(); for (Map<String, String> item : items) { this.trackEcommerceTransactionItem(order_id, mapCheck(item, "sku"), dParseCatch(mapCheck(item, "price")), iParseCatch(mapCheck(item, "quantity")), mapCheck(item, "name"), mapCheck(item, "category"), mapCheck(item, "currency"), null, this.payload.getParam("tid")); } } /* Checker or Helper functions * doubleCheck is used to catch fields that aren't required but double cant be null * and it gets messier handling it with Integers that can be null */ private String integerCheck(Integer i) { return i == null ? "" : String.valueOf(i); } private String doubleCheck(Double d) { return d == null ? "" : String.valueOf(d); } private String stringCheck(String s) { return s == null ? "" : s; } private String mapCheck(Map<String, String> m, String s) { return m.containsKey(s) ? m.get(s) : ""; } private double dParseCatch(String s) { try { return Double.parseDouble(s); } catch (NumberFormatException nfe) { throw new NumberFormatException("Item requires fields: 'sku', 'price','quantity'"); } } private int iParseCatch(String s) { try { return Integer.parseInt(s); } catch (NumberFormatException nfe) { throw new NumberFormatException("Item requires fields: 'sku', 'price','quantity'"); } } /** * {@inheritDoc} * @param emitter emitter to be added. */ public void setEmitter(Emitter emitter) { this.emitter = emitter; } //Turn String input into JSONObject private JSONObject stringToJSON(String jsonStr) throws JSONException { return new JSONObject(jsonStr); } private JSONObject mapToJSON(Map<String, Object> oMap) { return new JSONObject(oMap); } //Clear Payload for next iterations private void clearPayload() { String[] standards = new String[] { "uid", "res", "vp", "cd", "tz", "br_lang", "os_nm", "os_fam" }; PayloadMap standPairs = new PayloadMapC(); Map<String, String> currentParam = this.payload.getParams(); for (String s : standards) if (currentParam.containsKey(s)) standPairs.add(s, currentParam.get(s)); this.setPayload(standPairs); } private void setPayload(PayloadMap payload) { this.payload = payload; this.payload = this.payload.addConfig("encode_base64", this.base64_encode); setStandardNV(); } //Only called once when the Payload class is attacked to the com.com.saggezza.com.saggezza.jtracker.track.Tracker private void setStandardNV() { this.payload = this.payload.addStandardNVPairs(DEFAULT_PLATFORM, VERSION, this.namespace, this.app_id); } /** * {@inheritDoc} * @param param Parameter to be set. * @param val Value for the parameter. */ public void setParam(String param, String val) { this.payload = this.payload.add(param, val); } /** * {@inheritDoc} * @param param Configuration to be set. * @param val Value for the parameter. */ public void setConfig(String param, boolean val) { this.payload = this.payload.addConfig(param, val); } /** * {@inheritDoc} * @param platform The platform being tracked, currently supports "pc", "tv", "mob", "cnsl", and "iot". */ public void setPlatform(String platform) {//contract true assert this.stringContractor.checkContract(this.contracts, PlowContractor.isSupportedPlatform, platform); this.payload = this.payload.add("p", platform); } /** * {@inheritDoc} * @param userID The User ID String. */ public void setUserID(String userID) { assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, userID); this.payload = this.payload.add("uid", userID); } /** * {@inheritDoc} * @param width Width of the screen in pixels. * @param height Height of the screen in pixels. */ public void setScreenResolution(int width, int height) { assert this.integerContractor.checkContract(this.contracts, PlowContractor.positiveNumber, height); assert this.integerContractor.checkContract(this.contracts, PlowContractor.positiveNumber, width); this.payload = this.payload.add("res", String.valueOf(width) + "x" + String.valueOf(height)); } /** * {@inheritDoc} * @param width Width of the viewport in pixels. * @param height Height of the viewport in pixels. */ public void setViewport(int width, int height) { assert this.integerContractor.checkContract(this.contracts, PlowContractor.positiveNumber, height); assert this.integerContractor.checkContract(this.contracts, PlowContractor.positiveNumber, width); this.payload = this.payload.add("vp", String.valueOf(width) + "x" + String.valueOf(height)); } /** * {@inheritDoc} * @param depth Depth of the color. */ public void setColorDepth(int depth) { assert this.integerContractor.checkContract(this.contracts, PlowContractor.positiveNumber, depth) || depth == 0; this.payload = this.payload.add("cd", String.valueOf(depth)); } /** * {@inheritDoc} * @param timezone Timezone where tracking takes place. */ public void setTimezone(String timezone) { assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, timezone); this.payload = this.payload.add("tz", timezone); } /** * {@inheritDoc} * @param language Language for info tracked. */ public void setLanguage(String language) { assert this.stringContractor.checkContract(this.contracts, PlowContractor.nonEmptyString, language); this.payload = this.payload.add("br_lang", language); } /** * {@inheritDoc} * @return */ public PayloadMap getPayload() { return this.payload; } /** * {@inheritDoc} */ public void terminateExecutor() { this.executor.shutdown(); } //Test case main function public static void main(String[] args) throws IOException, JSONException, URISyntaxException { ///// Setup ///// CONFIGURATIONS TrackerC.debug = true; TrackerC.track = false; TrackerC.singleVar = true; ///// EmitterC Emitter emitter = new Emitter("localhost:80", "/javaplow"); ///// REGULAR TRACKER Tracker t1 = new TrackerC(emitter, "Tracker Test", "JavaPlow", "com.com.saggezza", true, true); // t1.setUserID("User1"); t1.setLanguage("eng"); t1.setPlatform("pc"); t1.setScreenResolution(1260, 1080); String context = "{'Zone':'USA', 'Phone':'Droid', 'Time':'2pm'}"; ///// E COMMERCE TEST Map<String, String> items = new HashMap<String, String>(); items.put("sku", "SKUVAL"); items.put("quantity", "2"); items.put("price", "19.99"); List<Map<String, String>> lst = new LinkedList<Map<String, String>>(); lst.add(items); ///// GENERICS GenericTracker t2 = new TrackerC(emitter, "GenericTracker Test", "JavaPlow", "com.com.saggezza", true, true); t2.setLanguage("English"); t2.setPlatform("pc"); t2.setScreenResolution(1200, 1080); ///// GENERIC MAP Map<String, Object> dict = new LinkedHashMap<String, Object>(); dict.put("Username", System.getProperty("user.name")); dict.put("OperatingSystem", System.getProperty("os.name")); dict.put("OS_Version", System.getProperty("os.version")); dict.put("JRE_Version", System.getProperty("java.version")); /////TRACK TEST for (int i = 0; i < 15; i++) { try { Thread.sleep(3000); } catch (InterruptedException e) { } System.out.println("Loop " + i); dict.put("Iteration", i); // System.out.println(dict.toString() + "\n" + t2.getPayload().toString()); // t2.setupTrack("Kevin"); // t2.trackGenericEvent("Lube Insights", "Data Loop", dict, context); t1.setupTrack("Kevin", new JSONObject()); // t1.trackEcommerceTransactionItem("IT1023", "SKUVAL", 29.99, 2, "boots", "Shoes","USD",null,null); t1.trackEcommerceTransaction("OID", 19.99, "Kohls", 2.50, 1.99, "Chagrin", "OH", "USA", "USD", lst, context); } } }