X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fjalview%2Fanalytics%2FPlausible.java;h=ab2de77c24fcbc4c2aabf026a6633011980530d7;hb=73351640358c1b1fafd2612ac91b706ec4f80f7d;hp=0d6cd651456c56d6a255ecf2ca605feb3a0d9828;hpb=16f9a5d9288821419534eecbdba9eda073196f3b;p=jalview.git diff --git a/src/jalview/analytics/Plausible.java b/src/jalview/analytics/Plausible.java index 0d6cd65..ab2de77 100644 --- a/src/jalview/analytics/Plausible.java +++ b/src/jalview/analytics/Plausible.java @@ -1,3 +1,23 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ package jalview.analytics; import java.io.BufferedReader; @@ -19,24 +39,28 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Random; import jalview.bin.Cache; import jalview.bin.Console; -import jalview.bin.Jalview; import jalview.util.ChannelProperties; +import jalview.util.HttpUtils; public class Plausible { - private static final String USER_AGENT = ChannelProperties - .getProperty("app_name", "Jalview") + " " - + Cache.getDefault("VERSION", "Unknown") + " " - + MethodHandles.lookup().lookupClass() + " help@jalview.org"; + private static final String USER_AGENT; private static final String JALVIEW_ID = "Jalview Desktop"; private static final String DOMAIN = "jalview.org"; - private static final String API_BASE_URL = "https://plausible.io/api/event"; + private static final String CONFIG_API_BASE_URL = "https://www.jalview.org/services/config/analytics/url"; + + private static final String DEFAULT_API_BASE_URL = "https://analytics.jalview.org/api/event"; + + private static final String API_BASE_URL; + + private static final String clientId; public static final String APPLICATION_BASE_URL = "desktop://localhost"; @@ -44,8 +68,6 @@ public class Plausible private List> jsonObject; - private List events; - private List> cookieValues; private static boolean ENABLED = false; @@ -54,36 +76,55 @@ public class Plausible private static Plausible instance = null; - private static final Map defaultParams; + private static final Map defaultProps; static { - defaultParams = new HashMap<>(); - defaultParams.put("app_name", + defaultProps = new HashMap<>(); + defaultProps.put("app_name", ChannelProperties.getProperty("app_name") + " Desktop"); - defaultParams.put("version", Cache.getProperty("VERSION")); - defaultParams.put("build_date", + defaultProps.put("version", Cache.getProperty("VERSION")); + defaultProps.put("build_date", Cache.getDefault("BUILD_DATE", "unknown")); - defaultParams.put("java_version", System.getProperty("java.version")); + defaultProps.put("java_version", System.getProperty("java.version")); String val = System.getProperty("sys.install4jVersion"); if (val != null) { - defaultParams.put("install4j_version", val); + defaultProps.put("install4j_version", val); } val = System.getProperty("installer_template_version"); if (val != null) { - defaultParams.put("install4j_template_version", val); + defaultProps.put("install4j_template_version", val); } val = System.getProperty("launcher_version"); if (val != null) { - defaultParams.put("launcher_version", val); + defaultProps.put("launcher_version", val); } - defaultParams.put("java_arch", + defaultProps.put("java_arch", System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version")); + defaultProps.put("os", System.getProperty("os.name")); + defaultProps.put("os_version", System.getProperty("os.version")); + defaultProps.put("os_arch", System.getProperty("os.arch")); + String installation = Cache.applicationProperties + .getProperty("INSTALLATION"); + if (installation != null) + { + defaultProps.put("installation", installation); + } + + // ascertain the API_BASE_URL + API_BASE_URL = getAPIBaseURL(); + + // random clientId to make User-Agent unique (to register analytic) + clientId = String.format("%08x", new Random().nextInt()); + + USER_AGENT = HttpUtils.getUserAgent( + MethodHandles.lookup().lookupClass().getCanonicalName() + " " + + clientId); } private Plausible() @@ -96,29 +137,28 @@ public class Plausible ENABLED = b; } - public void sendEvent(String eventName, String path, - String... paramsStrings) + public void sendEvent(String eventName, String urlString, + String... propsStrings) { - sendEvent(eventName, path, false, paramsStrings); + sendEvent(eventName, urlString, false, propsStrings); } /** * The simplest way to send an analytic event. * * @param eventName - * The event name. To emulate a webpage view use "page_view" and set - * a "page_location" parameter. See - * https://developers.google.com/analytics/devguides/collection/ga4/events?client_type=gtag - * @param sendDefaultParams - * Flag whether to add the default params about the application. - * @param paramsStrings + * The event name. To emulate a webpage view use "pageview" and set a + * "url" key/value. See https://plausible.io/docs/events-api + * @param sendDefaultProps + * Flag whether to add the default props about the application. + * @param propsStrings * Optional multiple Strings in key, value pairs (there should be an - * even number of paramsStrings) to be set as parameters of the - * event. To emulate a webpage view use "page_location" as the URL in - * a "page_view" event. + * even number of propsStrings) to be set as property of the event. + * To emulate a webpage view set "url" as the URL in a "pageview" + * event. */ - public void sendEvent(String eventName, String path, - boolean sendDefaultParams, String... paramsStrings) + public void sendEvent(String eventName, String urlString, + boolean sendDefaultProps, String... propsStrings) { // clear out old lists this.resetLists(); @@ -128,45 +168,40 @@ public class Plausible Console.debug("Plausible not enabled."); return; } - Map params = new HashMap<>(); + Map props = new HashMap<>(); // add these to all events from this application instance - if (sendDefaultParams) + if (sendDefaultProps) { - params.putAll(defaultParams); - if (Jalview.isHeadlessMode()) - { - params.put("headless", "true"); - } + props.putAll(defaultProps); } - // add (and overwrite with) the passed in params - if (paramsStrings != null && paramsStrings.length > 0) + // add (and overwrite with) the passed in props + if (propsStrings != null && propsStrings.length > 0) { - if (paramsStrings.length % 2 != 0) + if (propsStrings.length % 2 != 0) { Console.warn( - "Cannot addEvent with odd number of paramsStrings. Ignoring the last one."); + "Cannot addEvent with odd number of propsStrings. Ignoring the last one."); } - for (int i = 0; i < paramsStrings.length - 1; i += 2) + for (int i = 0; i < propsStrings.length - 1; i += 2) { - String key = paramsStrings[i]; - String value = paramsStrings[i + 1]; - params.put(key, value); + String key = propsStrings[i]; + String value = propsStrings[i + 1]; + props.put(key, value); } } addJsonValue("domain", DOMAIN); addJsonValue("name", eventName); - StringBuilder recordedUrlSb = new StringBuilder(APPLICATION_BASE_URL); - if (!APPLICATION_BASE_URL.endsWith("/") && !path.startsWith("/")) + StringBuilder eventUrlSb = new StringBuilder(APPLICATION_BASE_URL); + if (!APPLICATION_BASE_URL.endsWith("/") && !urlString.startsWith("/")) { - recordedUrlSb.append("/"); + eventUrlSb.append("/"); } - recordedUrlSb.append(path); - addJsonValue("url", recordedUrlSb.toString()); - addEvent(eventName, params); - addJsonObject("props", params); + eventUrlSb.append(urlString); + addJsonValue("url", eventUrlSb.toString()); + addJsonObject("props", props); StringBuilder urlSb = new StringBuilder(); urlSb.append(API_BASE_URL); String qs = buildQueryString(); @@ -187,6 +222,10 @@ public class Plausible Console.debug( "Plausible: HTTP Request is: '" + urlSb.toString() + "'"); + if (DEBUG) + { + Console.debug("Plausible: User-Agent is: '" + USER_AGENT + "'"); + } Console.debug("Plausible: POSTed JSON is:\n" + jsonString); byte[] jsonBytes = jsonString.getBytes(StandardCharsets.UTF_8); @@ -226,7 +265,7 @@ public class Plausible sb.append(response); } String body = sb.toString(); - Console.debug("Plausible response content; " + body); + Console.debug("Plausible response content:\n" + body); } } catch (MalformedURLException e) { @@ -247,20 +286,6 @@ public class Plausible } } - public void addEvent(String name, Map params) - { - Event event = new Event(name); - if (params != null && params.size() > 0) - { - for (String key : params.keySet()) - { - String value = params.get(key); - event.addParam(key, value); - } - } - events.add(event); - } - private void addJsonObject(String key, Map map) { List> list = new ArrayList<>(); @@ -311,7 +336,6 @@ public class Plausible private void resetLists() { jsonObject = new ArrayList<>(); - events = new ArrayList(); queryStringValues = new ArrayList<>(); cookieValues = new ArrayList<>(); } @@ -523,4 +547,56 @@ public class Plausible { return new AbstractMap.SimpleEntry(s, v); } -} \ No newline at end of file + + private static String getAPIBaseURL() + { + try + { + URL url = new URL(CONFIG_API_BASE_URL); + URLConnection urlConnection = url.openConnection(); + HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection; + httpURLConnection.setRequestMethod("GET"); + httpURLConnection.setRequestProperty("User-Agent", USER_AGENT); + httpURLConnection.setConnectTimeout(5000); + httpURLConnection.setReadTimeout(3000); + httpURLConnection.connect(); + int responseCode = httpURLConnection.getResponseCode(); + String responseMessage = httpURLConnection.getResponseMessage(); + + if (responseCode < 200 || responseCode > 299) + { + Console.warn("Config URL connection to '" + CONFIG_API_BASE_URL + + "' failed: '" + responseCode + " " + responseMessage + + "'"); + } + + BufferedReader br = new BufferedReader( + new InputStreamReader((httpURLConnection.getInputStream()))); + StringBuilder sb = new StringBuilder(); + String response; + while ((response = br.readLine()) != null) + { + sb.append(response); + } + if (sb.length() > 7 && sb.substring(0, 5).equals("https")) + { + return sb.toString(); + } + + } catch (MalformedURLException e) + { + Console.debug("Somehow the config URL is malformed: '" + + CONFIG_API_BASE_URL + "'", e); + } catch (IOException e) + { + Console.debug("Connection to Plausible BASE_URL '" + API_BASE_URL + + "' failed.", e); + } catch (ClassCastException e) + { + Console.debug( + "Couldn't cast URLConnection to HttpURLConnection in Plausible.", + e); + } + return DEFAULT_API_BASE_URL; + } +}