Merge branch 'develop' into improvement/JAL-4212_remove_macos_test_java_icons
[jalview.git] / src / jalview / bin / Cache.java
index da6b1ea..fc9ddda 100755 (executable)
@@ -42,7 +42,9 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Properties;
 import java.util.StringTokenizer;
 import java.util.TreeSet;
@@ -50,6 +52,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;
@@ -310,6 +313,24 @@ public class Cache
   // in-memory only storage of proxy password, safer to use char array
   public static char[] proxyAuthPassword = null;
 
+  /**
+   * Session properties, set by command line, try not to affect stored
+   * properties!
+   */
+  private static Map<String, String> sessionProperties = new HashMap<>();
+
+  private static boolean bypassSessionProperties = false;
+
+  public static void enableSessionProperties()
+  {
+    bypassSessionProperties = false;
+  }
+
+  public static void disableSessionProperties()
+  {
+    bypassSessionProperties = true;
+  }
+
   /** Jalview Properties */
   public static Properties applicationProperties = new Properties()
   {
@@ -383,10 +404,13 @@ public class Cache
         {
           // props file provided as URL
           fis = new URL(propertiesFile).openStream();
-          System.out.println(
-                  "Loading jalview properties from : " + propertiesFile);
-          System.out.println(
-                  "Disabling Jalview writing to user's local properties file.");
+          if (!Jalview.quiet())
+          {
+            System.out.println(
+                    "Loading jalview properties from : " + propertiesFile);
+            System.out.println(
+                    "Disabling Jalview writing to user's local properties file.");
+          }
           propsAreReadOnly = true;
         } catch (Exception ex)
         {
@@ -414,7 +438,8 @@ public class Cache
         fis.close();
       } catch (Exception ex)
       {
-        System.out.println("Error reading properties file: " + ex);
+        if (!Jalview.quiet())
+          System.out.println("Error reading properties file: " + ex);
       }
     }
 
@@ -470,7 +495,8 @@ public class Cache
       }
     } catch (Exception ex)
     {
-      System.out.println("Error reading author details: " + ex);
+      if (!Jalview.quiet())
+        System.out.println("Error reading author details: " + ex);
       authorDetails = null;
     }
     if (authorDetails == null)
@@ -533,8 +559,8 @@ public class Cache
           if (orgtimeout == null)
           {
             orgtimeout = "30";
-            System.out.println("# INFO: Setting default net timeout to "
-                    + orgtimeout + " seconds.");
+            Console.debug("Setting default net timeout to " + orgtimeout
+                    + " seconds.");
           }
           String remoteVersion = null;
           if (remoteBuildPropertiesUrl.startsWith("http"))
@@ -554,10 +580,13 @@ public class Cache
               remoteVersion = remoteBuildProperties.getProperty("VERSION");
             } catch (Exception ex)
             {
-              System.out.println(
-                      "Non-fatal exception when checking version at "
-                              + remoteBuildPropertiesUrl + ":");
-              System.out.println(ex);
+              if (!Jalview.quiet())
+              {
+                System.out.println(
+                        "Non-fatal exception when checking version at "
+                                + remoteBuildPropertiesUrl + ":");
+                System.out.println(ex);
+              }
               remoteVersion = getProperty("VERSION");
             }
           }
@@ -656,7 +685,8 @@ public class Cache
       }
     } catch (Exception ex)
     {
-      System.out.println("Error reading build details: " + ex);
+      if (!Jalview.quiet())
+        System.out.println("Error reading build details: " + ex);
       applicationProperties.remove("VERSION");
     }
     String codeVersion = getProperty("VERSION");
@@ -677,7 +707,7 @@ public class Cache
     if (printVersion && reportVersion)
     {
       System.out.println(ChannelProperties.getProperty("app_name")
-              + " Version: " + codeVersion + codeInstallation);
+              + " version: " + codeVersion + codeInstallation);
     }
   }
 
@@ -703,7 +733,21 @@ public class Cache
    */
   public static String getProperty(String key)
   {
-    String prop = applicationProperties.getProperty(key);
+    return getProperty(key, false);
+  }
+
+  public static String getProperty(String key,
+          boolean skipSessionProperties)
+  {
+    String prop = null;
+    if (!(skipSessionProperties || bypassSessionProperties))
+    {
+      prop = getSessionProperty(key);
+    }
+    if (prop == null)
+    {
+      prop = applicationProperties.getProperty(key);
+    }
     if (prop == null && Platform.isJS())
     {
       prop = applicationProperties.getProperty(Platform.getUniqueAppletID()
@@ -738,8 +782,9 @@ public class Cache
         def = Integer.parseInt(string);
       } catch (NumberFormatException e)
       {
-        System.out.println("Error parsing int property '" + property
-                + "' with value '" + string + "'");
+        if (!Jalview.quiet())
+          System.out.println("Error parsing int property '" + property
+                  + "' with value '" + string + "'");
       }
     }
 
@@ -772,16 +817,25 @@ public class Cache
     try
     {
       oldValue = applicationProperties.setProperty(key, obj);
-      if (propertiesFile != null && !propsAreReadOnly)
+      if (propertiesFile != null && !propsAreReadOnly
+      // don't rewrite if new value is same as old value
+              && !((obj == null && oldValue == null)
+                      || (obj != null && obj.equals(oldValue))))
       {
+        // reset the session property too
+        if (sessionProperties.containsKey(key))
+        {
+          sessionProperties.remove(key);
+        }
         FileOutputStream out = new FileOutputStream(propertiesFile);
         applicationProperties.store(out, "---JalviewX Properties File---");
         out.close();
       }
     } catch (Exception ex)
     {
-      System.out.println(
-              "Error setting property: " + key + " " + obj + "\n" + ex);
+      if (!Jalview.quiet())
+        System.out.println(
+                "Error setting property: " + key + " " + obj + "\n" + ex);
     }
     return oldValue;
   }
@@ -811,7 +865,8 @@ public class Cache
         out.close();
       } catch (Exception ex)
       {
-        System.out.println("Error saving properties: " + ex);
+        if (!Jalview.quiet())
+          System.out.println("Error saving properties: " + ex);
       }
     }
   }
@@ -908,93 +963,138 @@ public class Cache
 
   protected static Class jgoogleanalyticstracker = 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)
     {
-      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";
+      GoogleAnalytics4.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
       {
-        // 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)
+        path = "/" + String.join("/", URLEncoder.encode(appName, "UTF-8"),
+                URLEncoder.encode(version, "UTF-8"),
+                URLEncoder.encode(APPLICATION_STARTED, "UTF-8"));
+      } catch (UnsupportedEncodingException e)
       {
-        err = e;
+      */
+      path = ("/" + String.join("/", appName, version, APPLICATION_STARTED))
+              .replace(' ', '+');
+      /*
       }
-      if (re != null || ex != null || err != null)
+      */
+      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
+    {
+      if (tracker == null)
       {
-        if (re != null)
+        if (jgoogleanalyticstracker == null)
         {
-          Console.debug("Caught runtime exception in googletracker init:",
-                  re);
+          // 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;
+          }
         }
-        if (ex != null)
+        // now initialise tracker
+        Exception re = null, ex = null;
+        Error err = null;
+        String vrs = "No Version Accessible";
+        try
         {
-          Console.warn(
-                  "Failed to initialise GoogleTracker for Jalview Desktop with version "
-                          + vrs,
-                  ex);
+          // 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);
+          }
         }
-        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
    * 
@@ -1149,7 +1249,8 @@ public class Cache
         }
       } catch (Exception ex)
       {
-        System.out.println("Error loading User ColourFile\n" + ex);
+        if (!Jalview.quiet())
+          System.out.println("Error loading User ColourFile\n" + ex);
       }
     }
     if (!files.equals(coloursFound.toString()))
@@ -1651,7 +1752,9 @@ public class Cache
       file = new File(releasePropertiesFilename);
     }
 
-    if (filename == null || !file.exists())
+    if (filename == null)
+      return null;
+    if (!file.exists())
     {
       System.err.println("Could not load bootstrap preferences file '"
               + filename + "'");
@@ -1680,4 +1783,17 @@ public class Cache
     }
     return bootstrapProps;
   }
+
+  public static void setSessionProperty(String key, String val)
+  {
+    if (key != null)
+    {
+      sessionProperties.put(key, val);
+    }
+  }
+
+  public static String getSessionProperty(String key)
+  {
+    return key == null ? null : sessionProperties.get(key);
+  }
 }