From: James Procter Date: Wed, 5 Jul 2023 12:42:57 +0000 (+0100) Subject: Merge branch 'patch/JAL-2292_caseSensitivePAE' into develop X-Git-Tag: Release_2_11_4_0~253 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=a531ac7b834790181ea13394160b04b81c8772fd;hp=c9e6efcdf33bcd88bd19a48b2934c28e48892fab;p=jalview.git Merge branch 'patch/JAL-2292_caseSensitivePAE' into develop --- diff --git a/help/help/html/privacy.html b/help/help/html/privacy.html index 9ad61b5..8704ce8 100644 --- a/help/help/html/privacy.html +++ b/help/help/html/privacy.html @@ -34,35 +34,36 @@

Usage data is collected from the logs of various web services that the Jalview Desktop contacts through its normal operation. These are described below:

- -

+

Stopping Jalview from calling home
If you run Jalview in 'headless mode' via the command line, then the diff --git a/help/markdown/releases/release-2_11_2_7.md b/help/markdown/releases/release-2_11_2_7.md new file mode 100644 index 0000000..361d39a --- /dev/null +++ b/help/markdown/releases/release-2_11_2_7.md @@ -0,0 +1,12 @@ +--- +version: 2.11.2.7 +date: 2023-06-30 +channel: "release" +--- + +## New Features +- Jalview now reports usage statistics via Plausible.io + +## Issues Resolved +- PDB structures slow to view when Jalview Java console is open +- chains in PDB or mmCIF files with negative RESNUMs not correctly parsed diff --git a/help/markdown/whatsnew/whatsnew-2_11_2_7.md b/help/markdown/whatsnew/whatsnew-2_11_2_7.md new file mode 100644 index 0000000..f884134 --- /dev/null +++ b/help/markdown/whatsnew/whatsnew-2_11_2_7.md @@ -0,0 +1,5 @@ +Jalview 2.11.2.7 is a minor patch release - it includes patches affecting efficiency when importing structures and a small revision to the import processing of structures with negative residue numbering. + +With this release, Jalview usage statistics are now collected by a jalview.org hosted instance of the open source privacy-preserving analytics stack, Plausible.io. + + diff --git a/j11lib/JGoogleAnalytics_0.3.jar b/j11lib/JGoogleAnalytics_0.3.jar deleted file mode 100644 index 0dbc98c..0000000 Binary files a/j11lib/JGoogleAnalytics_0.3.jar and /dev/null differ diff --git a/j8lib/JGoogleAnalytics_0.3.jar b/j8lib/JGoogleAnalytics_0.3.jar deleted file mode 100644 index 0dbc98c..0000000 Binary files a/j8lib/JGoogleAnalytics_0.3.jar and /dev/null differ diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 7f5746e..924b9cb 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1452,8 +1452,8 @@ label.choose_tempfac_type = Choose Temperature Factor type label.interpret_tempfac_as = Interpret Temperature Factor as label.add_pae_matrix_file = Add PAE matrix file label.nothing_selected = Nothing selected -prompt.google_analytics_title = Jalview Usage Statistics -prompt.google_analytics = Do you want to help make Jalview better by enabling the collection of usage statistics with Google Analytics?\nYou can enable or disable usage tracking in the preferences. +prompt.analytics_title = Jalview Usage Statistics +prompt.analytics = Do you want to help make Jalview better by enabling the collection of usage statistics with Plausible analytics?\nYou can enable or disable usage tracking in the preferences. label.working_ellipsis = Working ... action.show_groups_on_matrix = Show groups on matrix action.show_groups_on_matrix_tooltip = When enabled, clusters defined on the matrix's associated tree or below the assigned threshold are shown as different colours on the matrix annotation row diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index a4594dc..150a407 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -1434,5 +1434,5 @@ label.tftype_default = Default label.tftype_plddt = pLDDT label.add_pae_matrix_file = Añadir un fichero de matriz PAE label.nothing_selected = Nada seleccionado -prompt.google_analytics_title = Jalview Estadísticas de Uso -prompt.google_analytics = ¿Quiere ayudar a mejorar Jalview habilitando la recopilación de estadísticas de uso con Google Analytics?\nPuede habilitar o deshabilitar el seguimiento de uso en las preferencias. +prompt.analytics_title = Jalview Estadísticas de Uso +prompt.analytics = ¿Quiere ayudar a mejorar Jalview habilitando la recopilación de estadísticas de uso con análisis Plausible?\nPuede habilitar o deshabilitar el seguimiento de uso en las preferencias. diff --git a/src/jalview/analysis/AlignmentUtils.java b/src/jalview/analysis/AlignmentUtils.java index 1158c53..6ab49b2 100644 --- a/src/jalview/analysis/AlignmentUtils.java +++ b/src/jalview/analysis/AlignmentUtils.java @@ -1512,7 +1512,7 @@ public class AlignmentUtils * @param alignment * the alignment to add them to * @param selectionGroup - * current selection group (or null if none) + * current selection group - may be null, if provided then any added annotation will be trimmed to just those columns in the selection group */ public static void addReferenceAnnotations( Map> annotations, @@ -1536,7 +1536,7 @@ public class AlignmentUtils * @param seq * @param ann * @param selectionGroup - * - may be null + * current selection group - may be null, if provided then any added annotation will be trimmed to just those columns in the selection group * @return annotation added to {@code seq and {@code alignment} */ public static AlignmentAnnotation addReferenceAnnotationTo( diff --git a/src/jalview/analytics/GoogleAnalytics4.java b/src/jalview/analytics/Plausible.java similarity index 55% rename from src/jalview/analytics/GoogleAnalytics4.java rename to src/jalview/analytics/Plausible.java index ba8a920..ab2de77 100644 --- a/src/jalview/analytics/GoogleAnalytics4.java +++ b/src/jalview/analytics/Plausible.java @@ -1,8 +1,31 @@ +/* + * 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; import java.io.IOException; +import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.lang.invoke.MethodHandles; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; @@ -17,75 +40,94 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; -import java.util.UUID; import jalview.bin.Cache; import jalview.bin.Console; import jalview.util.ChannelProperties; +import jalview.util.HttpUtils; -public class GoogleAnalytics4 +public class Plausible { + private static final String USER_AGENT; + private static final String JALVIEW_ID = "Jalview Desktop"; - private static final String SESSION_ID = new Random().toString(); + private static final String DOMAIN = "jalview.org"; - private static final String MEASUREMENT_ID = "G-6TMPHMXEQ0"; + private static final String CONFIG_API_BASE_URL = "https://www.jalview.org/services/config/analytics/url"; - private static final String API_SECRET = "Qb9NSbqkRDqizG6j2BBJ2g"; + private static final String DEFAULT_API_BASE_URL = "https://analytics.jalview.org/api/event"; - // This will generate a different CLIENT_ID each time the application is - // launched. Do we want to store it in .jalview_properties? - private static final String CLIENT_ID = UUID.randomUUID().toString(); + private static final String API_BASE_URL; - private static final String BASE_URL = "https://www.google-analytics.com/mp/collect"; + private static final String clientId; - private static final String DESKTOP_EVENT = "desktop_event"; + public static final String APPLICATION_BASE_URL = "desktop://localhost"; private List> queryStringValues; private List> jsonObject; - private List events; - private List> cookieValues; private static boolean ENABLED = false; - private static GoogleAnalytics4 instance = null; + private static boolean DEBUG = true; - private static final Map defaultParams; + private static Plausible instance = null; + + 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 GoogleAnalytics4() + private Plausible() { this.resetLists(); } @@ -95,72 +137,79 @@ public class GoogleAnalytics4 ENABLED = b; } - public void sendAnalytics(String eventName, String... paramsStrings) + public void sendEvent(String eventName, String urlString, + String... propsStrings) { - sendAnalytics(eventName, 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 sendAnalytics(String eventName, boolean sendDefaultParams, - String... paramsStrings) + public void sendEvent(String eventName, String urlString, + boolean sendDefaultProps, String... propsStrings) { // clear out old lists this.resetLists(); if (!ENABLED) { - Console.debug("GoogleAnalytics4 not enabled."); + Console.debug("Plausible not enabled."); return; } - Map params = new HashMap<>(); - params.put("event_category", DESKTOP_EVENT); - params.put("event_label", eventName); + Map props = new HashMap<>(); // add these to all events from this application instance - if (sendDefaultParams) + if (sendDefaultProps) { - params.putAll(defaultParams); + 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); } } - addEvent(eventName, params); - addQueryStringValue("measurement_id", MEASUREMENT_ID); - addQueryStringValue("api_secret", API_SECRET); - addJsonValue("client_id", CLIENT_ID); - addJsonValues("events", Event.toObjectList(events)); + addJsonValue("domain", DOMAIN); + addJsonValue("name", eventName); + StringBuilder eventUrlSb = new StringBuilder(APPLICATION_BASE_URL); + if (!APPLICATION_BASE_URL.endsWith("/") && !urlString.startsWith("/")) + { + eventUrlSb.append("/"); + } + eventUrlSb.append(urlString); + addJsonValue("url", eventUrlSb.toString()); + addJsonObject("props", props); StringBuilder urlSb = new StringBuilder(); - urlSb.append(BASE_URL); - urlSb.append('?'); - urlSb.append(buildQueryString()); + urlSb.append(API_BASE_URL); + String qs = buildQueryString(); + if (qs != null && qs.length() > 0) + { + urlSb.append('?'); + urlSb.append(qs); + } try { URL url = new URL(urlSb.toString()); @@ -171,15 +220,21 @@ public class GoogleAnalytics4 String jsonString = buildJson(); - Console.debug("GA4: HTTP Request is: '" + urlSb.toString() + "'"); - Console.debug("GA4: POSTed JSON is:\n" + jsonString); + 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); int jsonLength = jsonBytes.length; httpURLConnection.setFixedLengthStreamingMode(jsonLength); httpURLConnection.setRequestProperty("Content-Type", - "application/json; charset=UTF-8"); + "application/json"); + httpURLConnection.setRequestProperty("User-Agent", USER_AGENT); httpURLConnection.connect(); try (OutputStream os = httpURLConnection.getOutputStream()) { @@ -187,50 +242,63 @@ public class GoogleAnalytics4 } int responseCode = httpURLConnection.getResponseCode(); String responseMessage = httpURLConnection.getResponseMessage(); + if (responseCode < 200 || responseCode > 299) { - Console.warn("GoogleAnalytics4 connection failed: '" + responseCode - + " " + responseMessage + "'"); + Console.warn("Plausible connection failed: '" + responseCode + " " + + responseMessage + "'"); } else { - Console.debug("GoogleAnalytics4 connection succeeded: '" - + responseCode + " " + responseMessage + "'"); + Console.debug("Plausible connection succeeded: '" + responseCode + + " " + responseMessage + "'"); + } + + if (DEBUG) + { + BufferedReader br = new BufferedReader(new InputStreamReader( + (httpURLConnection.getInputStream()))); + StringBuilder sb = new StringBuilder(); + String response; + while ((response = br.readLine()) != null) + { + sb.append(response); + } + String body = sb.toString(); + Console.debug("Plausible response content:\n" + body); } } catch (MalformedURLException e) { Console.debug( - "Somehow the GoogleAnalytics4 BASE_URL and queryString is malformed.", + "Somehow the Plausible BASE_URL and queryString is malformed: '" + + urlSb.toString() + "'", e); return; } catch (IOException e) { - Console.debug("Connection to GoogleAnalytics4 BASE_URL '" + BASE_URL + Console.debug("Connection to Plausible BASE_URL '" + API_BASE_URL + "' failed.", e); } catch (ClassCastException e) { Console.debug( - "Couldn't cast URLConnection to HttpURLConnection in GoogleAnalytics4.", + "Couldn't cast URLConnection to HttpURLConnection in Plausible.", e); } } - public void addEvent(String name, Map params) + private void addJsonObject(String key, Map map) { - Event event = new Event(name); - if (params != null && params.size() > 0) + List> list = new ArrayList<>(); + for (String k : map.keySet()) { - for (String key : params.keySet()) - { - String value = params.get(key); - event.addParam(key, value); - } + list.add(stringEntry(k, map.get(k))); } - events.add(event); + addJsonObject(key, list); + } private void addJsonObject(String key, - List> object) + List> object) { jsonObject.add(objectEntry(key, object)); } @@ -268,16 +336,15 @@ public class GoogleAnalytics4 private void resetLists() { jsonObject = new ArrayList<>(); - events = new ArrayList(); queryStringValues = new ArrayList<>(); cookieValues = new ArrayList<>(); } - public static GoogleAnalytics4 getInstance() + public static Plausible getInstance() { if (instance == null) { - instance = new GoogleAnalytics4(); + instance = new Plausible(); } return instance; } @@ -480,52 +547,56 @@ public class GoogleAnalytics4 { return new AbstractMap.SimpleEntry(s, v); } -} - -class Event -{ - private String name; - private List> params; - - @SafeVarargs - public Event(String name, Map.Entry... paramEntries) + private static String getAPIBaseURL() { - this.name = name; - this.params = new ArrayList>(); - for (Map.Entry paramEntry : paramEntries) + try { - if (paramEntry == null) + 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) { - continue; + Console.warn("Config URL connection to '" + CONFIG_API_BASE_URL + + "' failed: '" + responseCode + " " + responseMessage + + "'"); } - params.add(paramEntry); - } - } - public void addParam(String param, String value) - { - params.add(GoogleAnalytics4.stringEntry(param, value)); - } + 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(); + } - protected List> toObject() - { - List> object = new ArrayList<>(); - object.add(GoogleAnalytics4.objectEntry("name", (Object) name)); - if (params.size() > 0) + } catch (MalformedURLException e) { - object.add(GoogleAnalytics4.objectEntry("params", (Object) params)); - } - return object; - } - - protected static List toObjectList(List events) - { - List eventObjectList = new ArrayList<>(); - for (Event event : events) + Console.debug("Somehow the config URL is malformed: '" + + CONFIG_API_BASE_URL + "'", e); + } catch (IOException e) { - eventObjectList.add((Object) event.toObject()); + 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 eventObjectList; + return DEFAULT_API_BASE_URL; } } diff --git a/src/jalview/api/RotatableCanvasI.java b/src/jalview/api/RotatableCanvasI.java index c6eb6de..1646d89 100644 --- a/src/jalview/api/RotatableCanvasI.java +++ b/src/jalview/api/RotatableCanvasI.java @@ -1,6 +1,6 @@ /* - * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1) - * Copyright (C) 2014 The Jalview Authors + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * diff --git a/src/jalview/bin/Cache.java b/src/jalview/bin/Cache.java index fc9ddda..5741908 100755 --- a/src/jalview/bin/Cache.java +++ b/src/jalview/bin/Cache.java @@ -43,6 +43,7 @@ import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; @@ -52,7 +53,7 @@ import java.util.TreeSet; import javax.swing.LookAndFeel; import javax.swing.UIManager; -import jalview.analytics.GoogleAnalytics4; +import jalview.analytics.Plausible; import jalview.datamodel.PDBEntry; import jalview.gui.Preferences; import jalview.gui.UserDefinedColours; @@ -128,7 +129,7 @@ import jalview.ws.sifts.SiftsSettings; * service *
  • QUESTIONNAIRE last questionnaire:responder id string from questionnaire * service
  • - *
  • USAGESTATS (false - user prompted) Enable google analytics tracker for + *
  • USAGESTATS (false - user prompted) Enable analytics tracker for * collecting usage statistics
  • *
  • SHOW_OVERVIEW boolean for overview window display
  • *
  • ANTI_ALIAS boolean for smooth fonts
  • @@ -954,146 +955,41 @@ public class Cache } /** - * GA tracker object - actually JGoogleAnalyticsTracker null if tracking not - * enabled. + * Initialise the tracker if it is not done already. */ - protected static Object tracker = null; - - protected static Class trackerfocus = null; - - protected static Class jgoogleanalyticstracker = null; - - private static boolean useGA4 = true; - - /** - * Initialise the google tracker if it is not done already. - */ - public static void initGoogleTracker() + public static void initAnalytics() { - if (useGA4) - { - GoogleAnalytics4.setEnabled(true); + Plausible.setEnabled(true); - String appName = ChannelProperties.getProperty("app_name") - + " Desktop"; - String version = Cache.getProperty("VERSION") + "_" - + Cache.getDefault("BUILD_DATE", "unknown"); - String path; - /* we don't want to encode ':' as "%3A" for backward compatibility with the UA setup - try - { - path = "/" + String.join("/", URLEncoder.encode(appName, "UTF-8"), - URLEncoder.encode(version, "UTF-8"), - URLEncoder.encode(APPLICATION_STARTED, "UTF-8")); - } catch (UnsupportedEncodingException e) - { - */ - path = ("/" + String.join("/", appName, version, APPLICATION_STARTED)) - .replace(' ', '+'); - /* - } - */ - GoogleAnalytics4 ga4 = GoogleAnalytics4.getInstance(); - - // This will add a page_view similar to the old UA analytics. - // We probably want to get rid of this once the application_launch event - // is being processed properly. - ga4.sendAnalytics("page_view", "page_location", path, "page_title", - APPLICATION_STARTED); - - // This will send a new "application_launch" event with parameters - // including the old-style "path", the channel name and version - ga4.sendAnalytics("application_launch", true, "page_location", path); - } - else + String appName = ChannelProperties.getProperty("app_name") + " Desktop"; + String version = Cache.getProperty("VERSION") + "_" + + Cache.getDefault("BUILD_DATE", "unknown"); + String path; + /* we don't want to encode ':' as "%3A" for backward compatibility with the UA setup + try { - if (tracker == null) - { - 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 - { - // 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) - { - 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); - } - } - else - { - Console.debug("Successfully initialised tracker."); - } - } + path = "/" + String.join("/", URLEncoder.encode(appName, "UTF-8"), + URLEncoder.encode(version, "UTF-8"), + URLEncoder.encode(APPLICATION_STARTED, "UTF-8")); + } catch (UnsupportedEncodingException e) + { + */ + List pathParts = new ArrayList<>(); + pathParts.add(appName); + pathParts.add(version); + pathParts.add(APPLICATION_STARTED); + path = ("/" + String.join("/", pathParts)).replace(' ', '+'); + /* } + */ + Plausible plausible = Plausible.getInstance(); + + // This will send a new "application_launch" event with parameters + // including the old-style "path", the channel name and version + plausible.sendEvent("application_launch", path, true); } - private static final String APPLICATION_STARTED = "Application Started."; + private static final String APPLICATION_STARTED = "Application Started"; /** * get the user's default colour if available diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index 3a733f3..57f2575 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -633,8 +633,7 @@ public class Jalview { headless = true; } - System.setProperty("http.agent", - "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown")); + System.setProperty("http.agent", HttpUtils.getUserAgent()); try { @@ -810,6 +809,7 @@ public class Jalview // Run Commands from cli cmds = new Commands(argparser, headlessArg); boolean commandsSuccess = cmds.argsWereParsed(); + if (commandsSuccess) { if (headlessArg) @@ -1573,7 +1573,7 @@ public class Jalview + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n" + "-noquestionnaire\tTurn off questionnaire check.\n" + "-nonews\tTurn off check for Jalview news.\n" - + "-nousagestats\tTurn off google analytics tracking for this session.\n" + + "-nousagestats\tTurn off analytics tracking for this session.\n" + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n" // + // "-setprop PROPERTY=VALUE\tSet the given Jalview property, @@ -1596,16 +1596,15 @@ public class Jalview */ PromptUserConfig prompter = new PromptUserConfig(Desktop.desktop, "USAGESTATS", - MessageManager.getString("prompt.google_analytics_title"), - MessageManager.getString("prompt.google_analytics"), + MessageManager.getString("prompt.plausible_analytics_title"), + MessageManager.getString("prompt.plausible_analytics"), new Runnable() { @Override public void run() { - Console.debug( - "Initialising googletracker for usage stats."); - Cache.initGoogleTracker(); + Console.debug("Initialising analytics for usage stats."); + Cache.initAnalytics(); Console.debug("Tracking enabled."); } }, new Runnable() @@ -1613,7 +1612,7 @@ public class Jalview @Override public void run() { - Console.debug("Not enabling Google Tracking."); + Console.debug("Not enabling analytics."); } }, null, true); desktop.addDialogThread(prompter); diff --git a/src/jalview/ext/jmol/JmolParser.java b/src/jalview/ext/jmol/JmolParser.java index c64dac1..57b406e 100644 --- a/src/jalview/ext/jmol/JmolParser.java +++ b/src/jalview/ext/jmol/JmolParser.java @@ -488,7 +488,7 @@ public class JmolParser extends StructureFile implements JmolStatusListener { int length = sq.getLength(); boolean ssFound = false; - Annotation asecstr[] = new Annotation[length + firstResNum - 1]; + Annotation asecstr[] = new Annotation[length + (firstResNum-sq.getStart())]; for (int p = 0; p < length; p++) { if (secstr[p] >= 'A' && secstr[p] <= 'z') diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index b901ae4..36d6434 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -3623,8 +3623,22 @@ public class Desktop extends jalview.jbgui.GDesktop { Desktop.instance.closeAll_actionPerformed(null); Desktop.instance.setVisible(false); - Desktop.instance.dispose(); + Desktop us = Desktop.instance; Desktop.instance = null; + // call dispose in a separate thread - try to avoid indirect deadlocks + new Thread(new Runnable() { + @Override + public void run() + { + ExecutorService dex = us.dialogExecutor; + if (dex!=null) { + dex.shutdownNow(); + us.dialogExecutor=null; + us.block.drainPermits(); + } + us.dispose(); + } + }).start(); } } diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index be9293f..88c1292 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -1734,10 +1734,9 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener protected void addReferenceAnnotations_actionPerformed( Map> candidates) { - final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup(); final AlignmentI alignment = this.ap.getAlignment(); AlignmentUtils.addReferenceAnnotations(candidates, alignment, - selectionGroup); + null); refresh(); } diff --git a/src/jalview/util/Comparison.java b/src/jalview/util/Comparison.java index 9fea705..e9ff931 100644 --- a/src/jalview/util/Comparison.java +++ b/src/jalview/util/Comparison.java @@ -337,9 +337,9 @@ public class Comparison // a long sequence. // check for at least 55% nucleotide, and nucleotide and ambiguity codes // (including N) must make up 95% - return ntCount * 100 > NUCLEOTIDE_COUNT_PERCENT * allCount + return ntCount * 100 >= NUCLEOTIDE_COUNT_PERCENT * allCount && 100 * (ntCount + nCount - + ntaCount) > NUCLEOTIDE_COUNT_LONG_SEQUENCE_AMBIGUITY_PERCENT + + ntaCount) >= NUCLEOTIDE_COUNT_LONG_SEQUENCE_AMBIGUITY_PERCENT * allCount; } else if (allCount > NUCLEOTIDE_COUNT_VERY_SHORT_SEQUENCE) @@ -347,7 +347,7 @@ public class Comparison // a short sequence. // check if a short sequence is at least 55% nucleotide and the rest of // the symbols are all X or all N - if (ntCount * 100 > NUCLEOTIDE_COUNT_PERCENT * allCount + if (ntCount * 100 >= NUCLEOTIDE_COUNT_PERCENT * allCount && (nCount == aaCount || xCount == aaCount)) { return true; diff --git a/src/jalview/util/HttpUtils.java b/src/jalview/util/HttpUtils.java index 0454cab..8379777 100644 --- a/src/jalview/util/HttpUtils.java +++ b/src/jalview/util/HttpUtils.java @@ -25,10 +25,11 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.ProtocolException; import java.net.URL; -import java.util.List; import javax.ws.rs.HttpMethod; +import jalview.bin.Cache; + public class HttpUtils { @@ -101,4 +102,46 @@ public class HttpUtils return connection.getResponseCode() == 200; } + public static String getUserAgent() + { + return getUserAgent(null); + } + + public static String getUserAgent(String className) + { + StringBuilder sb = new StringBuilder(); + sb.append("Jalview"); + sb.append('/'); + sb.append(Cache.getDefault("VERSION", "Unknown")); + sb.append(" ("); + sb.append(System.getProperty("os.name")); + sb.append("; "); + sb.append(System.getProperty("os.arch")); + sb.append(' '); + sb.append(System.getProperty("os.name")); + sb.append(' '); + sb.append(System.getProperty("os.version")); + sb.append("; "); + sb.append("java/"); + sb.append(System.getProperty("java.version")); + sb.append("; "); + sb.append("jalview/"); + sb.append(ChannelProperties.getProperty("channel")); + if (className != null) + { + sb.append("; "); + sb.append(className); + } + String installation = Cache.applicationProperties + .getProperty("INSTALLATION"); + if (installation != null) + { + sb.append("; "); + sb.append(installation); + } + sb.append(')'); + sb.append(" help@jalview.org"); + return sb.toString(); + } + }