JAL-4298 Fixed initial log level to see .jalview_properties setting. Moved Java Conso...
[jalview.git] / src / jalview / bin / Cache.java
index a75a27f..a44d474 100755 (executable)
@@ -24,7 +24,9 @@ import java.awt.Color;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
@@ -34,11 +36,16 @@ import java.net.PasswordAuthentication;
 import java.net.URL;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 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;
 import java.util.StringTokenizer;
 import java.util.TreeSet;
@@ -46,6 +53,7 @@ import java.util.TreeSet;
 import javax.swing.LookAndFeel;
 import javax.swing.UIManager;
 
+import jalview.analytics.Plausible;
 import jalview.datamodel.PDBEntry;
 import jalview.gui.Preferences;
 import jalview.gui.UserDefinedColours;
@@ -121,7 +129,7 @@ import jalview.ws.sifts.SiftsSettings;
  * service</li>
  * <li>QUESTIONNAIRE last questionnaire:responder id string from questionnaire
  * service</li>
- * <li>USAGESTATS (false - user prompted) Enable google analytics tracker for
+ * <li>USAGESTATS (false - user prompted) Enable analytics tracker for
  * collecting usage statistics</li>
  * <li>SHOW_OVERVIEW boolean for overview window display</li>
  * <li>ANTI_ALIAS boolean for smooth fonts</li>
@@ -133,6 +141,10 @@ import jalview.ws.sifts.SiftsSettings;
  * <li>WRAP_ALIGNMENT</li>
  * <li>EPS_RENDERING (Prompt each time|Lineart|Text) default for EPS rendering
  * style check</li>
+ * <li>BITMAP_SCALE - scale factor for PNG export - default 0.0 - native
+ * resolution</li>
+ * <li>BITMAP_HEIGHT - height bound for PNG export or 0 for unbound</li>
+ * <li>BITMAP_WIDTH - width bound for PNG export or 0 for unbound</li>
  * <li>SORT_ALIGNMENT (No sort|Id|Pairwise Identity)</li>
  * <li>SEQUENCE_LINKS list of name|URL pairs for opening a url with
  * $SEQUENCE_ID$</li>
@@ -240,6 +252,9 @@ public class Cache
    */
   public static final String JALVIEWLOGLEVEL = "logs.Jalview.level";
 
+  // for tests
+  public static final String BOOTSTRAP_TEST = "BOOTSTRAP_TEST";
+
   /**
    * Sifts settings
    */
@@ -303,6 +318,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()
   {
@@ -320,6 +353,8 @@ public class Cache
   /** Default file is ~/.jalview_properties */
   static String propertiesFile;
 
+  private static final String fallbackPropertiesFile = ".jalview_properties";
+
   private static boolean propsAreReadOnly = Platform.isJS();
 
   public static boolean isPropsAreReadOnly()
@@ -347,7 +382,7 @@ public class Cache
     {
       String channelPrefsFilename = ChannelProperties
               .getProperty("preferences.filename");
-      String releasePrefsFilename = ".jalview_properties";
+      String releasePrefsFilename = fallbackPropertiesFile;
       propertiesFile = System.getProperty("user.home") + File.separatorChar
               + channelPrefsFilename;
       releasePropertiesFile = System.getProperty("user.home")
@@ -374,10 +409,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())
+          {
+            jalview.bin.Console.outPrintln(
+                    "Loading jalview properties from : " + propertiesFile);
+            jalview.bin.Console.outPrintln(
+                    "Disabling Jalview writing to user's local properties file.");
+          }
           propsAreReadOnly = true;
         } catch (Exception ex)
         {
@@ -405,7 +443,9 @@ public class Cache
         fis.close();
       } catch (Exception ex)
       {
-        System.out.println("Error reading properties file: " + ex);
+        if (!Jalview.quiet())
+          jalview.bin.Console
+                  .outPrintln("Error reading properties file: " + ex);
       }
     }
 
@@ -461,7 +501,9 @@ public class Cache
       }
     } catch (Exception ex)
     {
-      System.out.println("Error reading author details: " + ex);
+      if (!Jalview.quiet())
+        jalview.bin.Console
+                .outPrintln("Error reading author details: " + ex);
       authorDetails = null;
     }
     if (authorDetails == null)
@@ -524,8 +566,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"))
@@ -545,10 +587,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())
+              {
+                jalview.bin.Console.errPrintln(
+                        "Non-fatal exception when checking version at "
+                                + remoteBuildPropertiesUrl + ":");
+                jalview.bin.Console.printStackTrace(ex);
+              }
               remoteVersion = getProperty("VERSION");
             }
           }
@@ -597,8 +642,8 @@ public class Cache
         url = Cache.class.getResource(resourcePath).toString();
       } catch (Exception ex)
       {
-        System.err.println("Failed to resolve resource " + resourcePath
-                + ": " + ex.getMessage());
+        jalview.bin.Console.errPrintln("Failed to resolve resource "
+                + resourcePath + ": " + ex.getMessage());
       }
     }
     else
@@ -647,7 +692,9 @@ public class Cache
       }
     } catch (Exception ex)
     {
-      System.out.println("Error reading build details: " + ex);
+      if (!Jalview.quiet())
+        jalview.bin.Console
+                .outPrintln("Error reading build details: " + ex);
       applicationProperties.remove("VERSION");
     }
     String codeVersion = getProperty("VERSION");
@@ -667,8 +714,9 @@ public class Cache
     new BuildDetails(codeVersion, null, codeInstallation);
     if (printVersion && reportVersion)
     {
-      System.out.println(ChannelProperties.getProperty("app_name")
-              + " Version: " + codeVersion + codeInstallation);
+      jalview.bin.Console
+              .outPrintln(ChannelProperties.getProperty("app_name")
+                      + " version: " + codeVersion + codeInstallation);
     }
   }
 
@@ -694,7 +742,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()
@@ -729,8 +791,28 @@ public class Cache
         def = Integer.parseInt(string);
       } catch (NumberFormatException e)
       {
-        System.out.println("Error parsing int property '" + property
-                + "' with value '" + string + "'");
+        if (!Jalview.quiet())
+          jalview.bin.Console.errPrintln("Error parsing int property '"
+                  + property + "' with value '" + string + "'");
+      }
+    }
+
+    return def;
+  }
+
+  public static float getDefault(String property, float def)
+  {
+    String string = getProperty(property);
+    if (string != null)
+    {
+      try
+      {
+        def = Float.parseFloat(string);
+      } catch (NumberFormatException e)
+      {
+        if (!Jalview.quiet())
+          jalview.bin.Console.errPrintln("Error parsing float property '"
+                  + property + "' with value '" + string + "'");
       }
     }
 
@@ -763,16 +845,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())
+        jalview.bin.Console.errPrintln(
+                "Error setting property: " + key + " " + obj + "\n" + ex);
     }
     return oldValue;
   }
@@ -802,7 +893,8 @@ public class Cache
         out.close();
       } catch (Exception ex)
       {
-        System.out.println("Error saving properties: " + ex);
+        if (!Jalview.quiet())
+          jalview.bin.Console.errPrintln("Error saving properties: " + ex);
       }
     }
   }
@@ -890,102 +982,42 @@ public class Cache
   }
 
   /**
-   * GA tracker object - actually JGoogleAnalyticsTracker null if tracking not
-   * enabled.
-   */
-  protected static Object tracker = null;
-
-  protected static Class trackerfocus = null;
-
-  protected static Class jgoogleanalyticstracker = null;
-
-  /**
-   * Initialise the google tracker if it is not done already.
+   * Initialise the tracker if it is not done already.
    */
-  public static void initGoogleTracker()
+  public static void initAnalytics()
   {
-    if (tracker == null)
+    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
     {
-      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<String> 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";
+
   /**
    * get the user's default colour if available
    * 
@@ -1052,8 +1084,9 @@ public class Cache
         return date_format.parse(val);
       } catch (Exception ex)
       {
-        System.err.println("Invalid or corrupt date in property '"
-                + propertyName + "' : value was '" + val + "'");
+        jalview.bin.Console
+                .errPrintln("Invalid or corrupt date in property '"
+                        + propertyName + "' : value was '" + val + "'");
       }
     }
     return null;
@@ -1076,8 +1109,8 @@ public class Cache
         return Integer.valueOf(val);
       } catch (NumberFormatException x)
       {
-        System.err.println("Invalid integer in property '" + property
-                + "' (value was '" + val + "')");
+        jalview.bin.Console.errPrintln("Invalid integer in property '"
+                + property + "' (value was '" + val + "')");
       }
     }
     return null;
@@ -1140,7 +1173,9 @@ public class Cache
         }
       } catch (Exception ex)
       {
-        System.out.println("Error loading User ColourFile\n" + ex);
+        if (!Jalview.quiet())
+          jalview.bin.Console
+                  .outPrintln("Error loading User ColourFile\n" + ex);
       }
     }
     if (!files.equals(coloursFound.toString()))
@@ -1614,4 +1649,87 @@ public class Cache
     String appbase = getGetdownAppbase();
     return appbase + "/" + getdownDistDir + "/build_properties";
   }
+
+  private static final Collection<String> bootstrapProperties = new ArrayList<>(
+          Arrays.asList(JALVIEWLOGLEVEL, BOOTSTRAP_TEST));
+
+  public static Properties bootstrapProperties(String filename)
+  {
+    Properties bootstrapProps = new Properties();
+    File file = null;
+    if (filename != null)
+    {
+      file = new File(filename);
+    }
+    if (file == null || !file.exists())
+    {
+      if (file != null)
+      {
+        jalview.bin.Console
+                .errPrintln("Could not load bootstrap preferences file '"
+                        + file.getPath() + "'");
+      }
+      String channelPrefsFilename = ChannelProperties
+              .getProperty("preferences.filename");
+      String propertiesFilename = System.getProperty("user.home")
+              + File.separatorChar + channelPrefsFilename;
+      jalview.bin.Console.errPrintln(
+              "Using default properties file '" + propertiesFilename + "'");
+      file = new File(propertiesFilename);
+    }
+    if (file == null || !file.exists())
+
+    {
+      String releasePrefsFilename = fallbackPropertiesFile;
+      String releasePropertiesFilename = System.getProperty("user.home")
+              + File.separatorChar + releasePrefsFilename;
+      jalview.bin.Console.errPrintln("Falling back to properties file '"
+              + releasePropertiesFilename + "'");
+      file = new File(releasePropertiesFilename);
+    }
+
+    if (!file.exists())
+    {
+      jalview.bin.Console
+              .errPrintln("Could not load bootstrap preferences file '"
+                      + file.getPath() + "'");
+      return null;
+    }
+
+    try
+    {
+      FileInputStream in = new FileInputStream(file.getAbsoluteFile());
+      Properties props = new Properties();
+      props.load(in);
+      for (String prop : bootstrapProperties)
+      {
+        if (props.containsKey(prop))
+          bootstrapProps.put(prop, props.getProperty(prop));
+      }
+    } catch (FileNotFoundException e)
+    {
+      jalview.bin.Console
+              .errPrintln("Could not find bootstrap preferences file '"
+                      + file.getAbsolutePath() + "'");
+    } catch (IOException e)
+    {
+      jalview.bin.Console.errPrintln(
+              "IOException when loading bootstrap preferences file '"
+                      + file.getAbsolutePath() + "'");
+    }
+    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);
+  }
 }