From deff57ef5fea596dda23366164478c4b41c34da9 Mon Sep 17 00:00:00 2001 From: Ben Soares Date: Mon, 5 Jun 2023 18:34:58 +0100 Subject: [PATCH] JAL-4001 untested GA4 client code --- src/jalview/analytics/GoogleAnalytics4.java | 208 +++++++++++++++++++++------ src/jalview/bin/Cache.java | 165 ++++++++++++--------- 2 files changed, 263 insertions(+), 110 deletions(-) diff --git a/src/jalview/analytics/GoogleAnalytics4.java b/src/jalview/analytics/GoogleAnalytics4.java index bd0949d..bf6f3f3 100644 --- a/src/jalview/analytics/GoogleAnalytics4.java +++ b/src/jalview/analytics/GoogleAnalytics4.java @@ -35,17 +35,45 @@ public class GoogleAnalytics4 private List> queryStringValues; - private List> jsonValues; + private List> jsonObject; + + private List events; private List> cookieValues; + private static boolean ENABLED = false; + public GoogleAnalytics4() { this.reset(); } + private static void setEnabled(boolean b) + { + ENABLED = b; + } + public void sendAnalytics() { + sendAnalytics(null); + } + + public void sendAnalytics(String path) + { + if (!ENABLED) + { + Console.debug("GoogleAnalytics4 not enabled."); + return; + } + if (path != null) + { + addEvent("page_event", path); + } + addQueryStringValue("measurement_id", MEASUREMENT_ID); + addQueryStringValue("api_secret", API_SECRET); + addJsonValue("client_id", CLIENT_ID); + addJsonValues("events", Event.toObjectList(events)); + // addJsonValue("events", ) StringBuilder sb = new StringBuilder(); sb.append(BASE_URL); sb.append('?'); @@ -69,7 +97,18 @@ public class GoogleAnalytics4 { os.write(jsonBytes); } - + int responseCode = httpURLConnection.getResponseCode(); + String responseMessage = httpURLConnection.getResponseMessage(); + if (responseCode < 200 || responseCode > 299) + { + Console.warn("GoogleAnalytics4 connection failed: '" + responseCode + + " " + responseMessage + "'"); + } + else + { + Console.debug("GoogleAnalytics4 connection succeeded: '" + + responseCode + " " + responseMessage + "'"); + } } catch (MalformedURLException e) { Console.debug( @@ -86,42 +125,66 @@ public class GoogleAnalytics4 "Couldn't cast URLConnection to HttpURLConnection in GoogleAnalytics4.", e); } + } + public void addEvent(String name, String... paramsStrings) + { + if (paramsStrings.length % 2 != 0) + { + Console.error( + "Cannot addEvent with odd number of paramsStrings. Ignoring."); + return; + } + Event event = new Event(name); + for (int i = 0; i < paramsStrings.length - 1; i += 2) + { + String key = paramsStrings[i]; + String value = paramsStrings[i + 1]; + event.addParam(key, value); + } + events.add(event); } - private void addToJson(String key, List values) + private void addJsonObject(String key, + List> object) { - jsonValues.add(jsonEntry(key, values)); + jsonObject.add(objectEntry(key, object)); } - private void addToJson(String key, String value) + private void addJsonValues(String key, List values) { - jsonValues.add(jsonEntry(key, value)); + jsonObject.add(objectEntry(key, values)); } - private void addToJson(String key, int value) + private void addJsonValue(String key, String value) { - jsonValues.add(jsonEntry(key, Integer.valueOf(value))); + jsonObject.add(objectEntry(key, value)); } - private void addToJson(String key, boolean value) + private void addJsonValue(String key, int value) { - jsonValues.add(jsonEntry(key, Boolean.valueOf(value))); + jsonObject.add(objectEntry(key, Integer.valueOf(value))); + } + + private void addJsonValue(String key, boolean value) + { + jsonObject.add(objectEntry(key, Boolean.valueOf(value))); } private void addQueryStringValue(String key, String value) { - queryStringValues.add(qsEntry(key, value)); + queryStringValues.add(stringEntry(key, value)); } private void addCookieValue(String key, String value) { - cookieValues.add(qsEntry(key, value)); + cookieValues.add(stringEntry(key, value)); } public void reset() { - jsonValues = new ArrayList<>(); + jsonObject = new ArrayList<>(); + events = new ArrayList(); queryStringValues = new ArrayList<>(); cookieValues = new ArrayList<>(); } @@ -151,30 +214,31 @@ public class GoogleAnalytics4 private String buildJson() { StringBuilder sb = new StringBuilder(); - int indent = 0; - sb.append("{"); - for (Map.Entry entry : jsonValues) - { - - } - sb.append("}"); + addJsonObject(sb, 0, jsonObject); return sb.toString(); } private void addJsonObject(StringBuilder sb, int indent, - Map.Entry entry) + List> entries) { - String key = entry.getKey(); - Object value = entry.getValue(); indent(sb, indent); - sb.append('"').append(key).append('"'); - sb.append(": "); - if (List.class.equals(value.getClass())) + sb.append("{\n"); + for (Map.Entry entry : entries) { - sb.append('\n'); + String key = entry.getKey(); + // TODO sensibly escape " characters in key + Object value = entry.getValue(); + indent(sb, indent); + sb.append('"').append(key).append('"'); + sb.append(": "); + if (List.class.equals(value.getClass())) + { + sb.append('\n'); + } + addJsonValue(sb, indent + 1, value); + sb.append(",\n"); } - addJsonValue(sb, indent, value); - sb.append(",\n"); + sb.append("}\n"); } private void addJsonValue(StringBuilder sb, int indent, Object value) @@ -188,21 +252,37 @@ public class GoogleAnalytics4 Class c = value.getClass(); if (Map.Entry.class.equals(c)) { - Map.Entry object = (Map.Entry) value; + Map.Entry entry = (Map.Entry) value; + List> object = new ArrayList<>(); + object.add(entry); addJsonObject(sb, indent + 1, object); } else if (List.class.equals(c)) { - indent(sb, indent); - sb.append("[\n"); - for (Object v : (List) value) + // list of Map.Entries or list of values? + List valueList = (List) value; + if (valueList.size() > 0 + && Map.Entry.class.equals(valueList.get(0).getClass())) + { + // entries + indent(sb, indent); + List> entryList = (List>) value; + addJsonObject(sb, indent + 1, entryList); + } + else { - indent(sb, indent + 1); - addJsonValue(sb, indent, v); - sb.append(",\n"); + // values + indent(sb, indent); + sb.append("[\n"); + for (Object v : valueList) + { + indent(sb, indent + 1); + addJsonValue(sb, indent + 1, v); + sb.append(",\n"); + } + indent(sb, indent); + sb.append("]"); } - indent(sb, indent); - sb.append("]"); } else if (String.class.equals(c)) { @@ -231,13 +311,61 @@ public class GoogleAnalytics4 sb.append(" ".repeat(indent)); } - private static Map.Entry jsonEntry(String s, Object o) + protected static Map.Entry objectEntry(String s, Object o) { return new AbstractMap.SimpleEntry(s, o); } - private static Map.Entry qsEntry(String s, String v) + protected static Map.Entry stringEntry(String s, String v) { return new AbstractMap.SimpleEntry(s, v); } } + +class Event +{ + private String name; + + private List> params; + + @SafeVarargs + public Event(String name, Map.Entry... paramEntries) + { + this.name = name; + this.params = new ArrayList>(); + for (Map.Entry paramEntry : paramEntries) + { + if (paramEntry == null) + { + continue; + } + params.add(paramEntry); + } + } + + public void addParam(String param, String value) + { + params.add(GoogleAnalytics4.stringEntry(param, value)); + } + + protected List> toObject() + { + List> object = new ArrayList<>(); + object.add(GoogleAnalytics4.objectEntry("name", (Object) name)); + if (params.size() > 0) + { + object.add(GoogleAnalytics4.objectEntry("params", (Object) params)); + } + return object; + } + + protected static List toObjectList(List events) + { + List eventObjectList = new ArrayList<>(); + for (Event event : events) + { + eventObjectList.add((Object) event.toObject()); + } + return eventObjectList; + } +} diff --git a/src/jalview/bin/Cache.java b/src/jalview/bin/Cache.java index 6b33fea..b03da07 100755 --- a/src/jalview/bin/Cache.java +++ b/src/jalview/bin/Cache.java @@ -50,6 +50,7 @@ import java.util.TreeSet; import javax.swing.LookAndFeel; import javax.swing.UIManager; +import jalview.analytics.GoogleAnalytics4; import jalview.datamodel.PDBEntry; import jalview.gui.Preferences; import jalview.gui.UserDefinedColours; @@ -920,93 +921,116 @@ public class Cache protected static Class jgoogleanalyticstracker = null; + private static GoogleAnalytics4 gaTracker = null; + + private static boolean useGA4 = true; + /** * Initialise the google tracker if it is not done already. */ public static void initGoogleTracker() { - if (tracker == null) + if (useGA4) + { + String appName = ChannelProperties.getProperty("app_name") + + " Desktop"; + String version = Cache.getProperty("VERSION") + "_" + + Cache.getDefault("BUILD_DATE", "unknown"); + String path = "/" + + String.join("/", appName, version, APPLICATION_STARTED); + gaTracker = new GoogleAnalytics4(); + gaTracker.sendAnalytics(path); + } + else { - if (jgoogleanalyticstracker == null) + if (tracker == null) { - // try to get the tracker class + if (jgoogleanalyticstracker == null) + { + // try to get the tracker class + try + { + jgoogleanalyticstracker = Cache.class.getClassLoader() + .loadClass( + "com.boxysystems.jgoogleanalytics.JGoogleAnalyticsTracker"); + trackerfocus = Cache.class.getClassLoader().loadClass( + "com.boxysystems.jgoogleanalytics.FocusPoint"); + } catch (Exception e) + { + Console.debug( + "com.boxysystems.jgoogleanalytics package is not present - tracking not enabled."); + tracker = null; + jgoogleanalyticstracker = null; + trackerfocus = null; + return; + } + } + // now initialise tracker + Exception re = null, ex = null; + Error err = null; + String vrs = "No Version Accessible"; try { - jgoogleanalyticstracker = Cache.class.getClassLoader().loadClass( - "com.boxysystems.jgoogleanalytics.JGoogleAnalyticsTracker"); - trackerfocus = Cache.class.getClassLoader() - .loadClass("com.boxysystems.jgoogleanalytics.FocusPoint"); + // Google analytics tracking code for Library Finder + tracker = jgoogleanalyticstracker + .getConstructor(new Class[] + { String.class, String.class, String.class }) + .newInstance(new Object[] + { ChannelProperties.getProperty("app_name") + " Desktop", + (vrs = Cache.getProperty("VERSION") + "_" + + Cache.getDefault("BUILD_DATE", "unknown")), + "UA-9060947-1" }); + jgoogleanalyticstracker + .getMethod("trackAsynchronously", new Class[] + { trackerfocus }) + .invoke(tracker, new Object[] + { trackerfocus + .getConstructor(new Class[] + { String.class }) + .newInstance(new Object[] + { APPLICATION_STARTED }) }); + } catch (RuntimeException e) + { + re = e; } catch (Exception e) { - Console.debug( - "com.boxysystems.jgoogleanalytics package is not present - tracking not enabled."); - tracker = null; - jgoogleanalyticstracker = null; - trackerfocus = null; - return; - } - } - // now initialise tracker - Exception re = null, ex = null; - Error err = null; - String vrs = "No Version Accessible"; - try - { - // Google analytics tracking code for Library Finder - tracker = jgoogleanalyticstracker - .getConstructor(new Class[] - { String.class, String.class, String.class }) - .newInstance(new Object[] - { ChannelProperties.getProperty("app_name") + " Desktop", - (vrs = Cache.getProperty("VERSION") + "_" - + Cache.getDefault("BUILD_DATE", "unknown")), - "UA-9060947-1" }); - jgoogleanalyticstracker - .getMethod("trackAsynchronously", new Class[] - { trackerfocus }) - .invoke(tracker, new Object[] - { trackerfocus.getConstructor(new Class[] { String.class }) - .newInstance(new Object[] - { "Application Started." }) }); - } catch (RuntimeException e) - { - re = e; - } catch (Exception e) - { - ex = e; - } catch (Error e) - { - err = e; - } - if (re != null || ex != null || err != null) - { - if (re != null) + ex = e; + } catch (Error e) { - Console.debug("Caught runtime exception in googletracker init:", - re); + err = e; } - if (ex != null) + if (re != null || ex != null || err != null) { - Console.warn( - "Failed to initialise GoogleTracker for Jalview Desktop with version " - + vrs, - ex); + if (re != null) + { + Console.debug("Caught runtime exception in googletracker init:", + re); + } + if (ex != null) + { + Console.warn( + "Failed to initialise GoogleTracker for Jalview Desktop with version " + + vrs, + ex); + } + if (err != null) + { + Console.error( + "Whilst initing GoogleTracker for Jalview Desktop version " + + vrs, + err); + } } - if (err != null) + else { - Console.error( - "Whilst initing GoogleTracker for Jalview Desktop version " - + vrs, - err); + Console.debug("Successfully initialised tracker."); } } - else - { - Console.debug("Successfully initialised tracker."); - } } } + private static final String APPLICATION_STARTED = "Application Started."; + /** * get the user's default colour if available * @@ -1436,10 +1460,11 @@ public class Cache if (customProxySet && // we have a username but no password for the scheme being // requested - (protocol.equalsIgnoreCase("http") - && (httpUser != null && httpUser.length() > 0 - && (httpPassword == null - || httpPassword.length == 0))) + (protocol.equalsIgnoreCase("http") + && (httpUser != null + && httpUser.length() > 0 + && (httpPassword == null + || httpPassword.length == 0))) || (protocol.equalsIgnoreCase("https") && (httpsUser != null && httpsUser.length() > 0 -- 1.7.10.2