JAL-3631 Spot system property triggers in getdown to create/activate using a user...
authorBen Soares <b.soares@dundee.ac.uk>
Wed, 12 Jun 2024 22:22:17 +0000 (23:22 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Wed, 12 Jun 2024 22:22:17 +0000 (23:22 +0100)
13 files changed:
getdown/lib/getdown-core.jar
getdown/lib/getdown-launcher-local.jar
getdown/lib/getdown-launcher.jar
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Digest.java
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java
getdown/src/getdown/core/src/main/java/jalview/util/ChannelProperties.java
getdown/src/getdown/core/src/main/java/jalview/util/LaunchUtils.java
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java
j11lib/getdown-core.jar
j8lib/getdown-core.jar

index 8c9e44b..6c20166 100644 (file)
Binary files a/getdown/lib/getdown-core.jar and b/getdown/lib/getdown-core.jar differ
index a3528ac..02d1a13 100644 (file)
Binary files a/getdown/lib/getdown-launcher-local.jar and b/getdown/lib/getdown-launcher-local.jar differ
index f803c4f..7862c28 100644 (file)
Binary files a/getdown/lib/getdown-launcher.jar and b/getdown/lib/getdown-launcher.jar differ
index f83add3..52876f0 100644 (file)
@@ -19,6 +19,8 @@ import java.net.URLEncoder;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.nio.channels.OverlappingFileLockException;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
 import java.security.*;
 import java.security.cert.Certificate;
 import java.util.*;
@@ -35,7 +37,7 @@ import jalview.util.LaunchUtils;
 import com.threerings.getdown.util.*;
 // avoid ambiguity with java.util.Base64 which we can't use as it's 1.8+
 import com.threerings.getdown.util.Base64;
-
+import com.threerings.getdown.Log;
 import com.threerings.getdown.data.EnvConfig;
 import com.threerings.getdown.data.EnvConfig.Note;
 
@@ -1090,7 +1092,7 @@ public class Application
         // add the marker indicating the app is running in getdown
         args.add("-D" + Properties.GETDOWN + "=true");
         args.add("-Dsys.install4jVersion=" + Application.i4jVersion);
-        args.add("-Dinstaller_template_version=" + System.getProperty("installer_template_version"));
+        args.add("-Dinstaller.template_version=" + System.getProperty("installer.template_version"));
         args.add("-Dlauncher_version=" + Build.version());
 
         // set HiDPI property if wanted
@@ -2062,6 +2064,135 @@ public class Application
        return _appbase;
     }
     
+    protected static boolean copyApplicationAppDirToUserAppDir(String applicationAppDirName, String userAppDirName) {
+      System.out.println("##### About to run copyApplicationAppDirToUserAppDir");
+      System.out.println("##### applicationAppDirName='"+applicationAppDirName+"'");
+      System.out.println("##### userAppDirName='"+userAppDirName+"'");
+      if (applicationAppDirName == null || userAppDirName == null) {
+        log.warning("Null parameter", "applicationAppDirName", applicationAppDirName, "userAppDirName", userAppDirName);
+        return false;
+      }
+      File applicationAppDir = new File(applicationAppDirName);
+      File userAppDir = new File(userAppDirName);
+      if (!userAppDir.exists()) {
+        userAppDir.mkdirs();
+      }
+      if (!(applicationAppDir.isDirectory() && userAppDir.isDirectory())) {
+        log.warning("Parameter not a directory", "applicaitonAppDirName", applicationAppDirName, "userAppDirName", userAppDirName);
+        return false;
+      }
+      File configFile = new File(applicationAppDir, CONFIG_FILE);
+      File userConfigFile = new File(userAppDir, CONFIG_FILE);
+      
+      if (userConfigFile.exists()) {
+        // We don't need to do this
+        return true;
+      }
+      
+      if (!configFile.isFile()) {
+        log.warning("Config file path not a file", "config", configFile);
+        return false;
+      }
+      
+      Config.ParseOpts opts = Config.createOpts(false);
+      Config config;
+      try {
+        config = Config.parseConfig(configFile, opts);
+      } catch (IOException e) {
+        log.warning("Problem opening application config file", configFile);
+        return false;
+      }
+      // get all types of local resource
+      String[] resourceNames = { "code", "ucode", "resource", "uresource", "xresource", "presource", "nresource" };
+      List<Resource> copyResources = new ArrayList<>();
+      for (String resourceName: resourceNames) {
+        parseSimpleLocalResources(config, resourceName, applicationAppDir.getPath(), copyResources, resourceName.equals("xresource"));
+      }
+      Digest digest2;
+      Digest digest;
+      try {
+        digest2 = new Digest(applicationAppDir, true);
+        digest = new Digest(applicationAppDir, 1, true);
+      } catch (IOException e) {
+        log.warning("Problem opening application digest files", new File(applicationAppDir, Digest.digestFile(Digest.VERSION)));
+        return false;
+      }
+      // copy getdown.txt, digest.txt and digest2.txt
+      for (File from: new File[] {configFile, digest2.getFile(), digest.getFile()}) {
+        try {
+          File to = new File(userAppDir, from.getName());
+          Files.copy(from.toPath(), to.toPath());
+        } catch (IOException e) {
+          log.warning("Couldn't copy config/digest file", from);
+        }
+      }
+      
+      MessageDigest md = Digest.getMessageDigest(Digest.VERSION);
+      for (Resource rsc: copyResources) {
+        System.out.println("##### resource: "+rsc.toString());
+        String digestHash = digest2.getDigest(rsc);
+        String actualHash;
+        try {
+          actualHash = rsc.computeDigest(Digest.VERSION, md, null);
+        } catch(IOException e) {
+          log.warning("Failed digest hash creation for resource", rsc);
+          continue;
+        }
+        System.out.println("####### digest "+rsc.getPath()+": "+digestHash);
+        System.out.println("####### actual "+rsc.getPath()+": "+actualHash);
+        if (digestHash == null || actualHash == null) {
+          log.warning("Something went wrong with digest hashes", digestHash, actualHash);
+          continue;
+        }
+        if (!digestHash.equals(actualHash)) {
+          log.warning("Digest does not match for resource", rsc);
+          continue;
+        }
+        File from = new File(applicationAppDir, rsc.getPath());
+        File to = new File(userAppDir, rsc.getPath());
+        if (!from.exists()) {
+          log.warning("Resource does not exist", rsc);
+          continue;
+        }
+        if (!to.getParentFile().exists()) {
+          to.getParentFile().mkdirs();
+        }
+        try {
+          Files.copy(from.toPath(), to.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
+          log.info("Copying resource file", from, to);
+          System.out.println("##### Copying file '"+from.toString()+" to "+to.toString());
+          try {
+            rsc.applyAttrs();
+          } catch(IOException e2) {
+            log.error("Could not apply attributes to resource", rsc);
+          }
+        } catch(IOException e) {
+          log.warning("Could not copy resource file", from, to);
+          continue;
+        }
+      }
+      
+      return true;
+    }
+    
+    protected static void parseSimpleLocalResources (Config config, String name, String applicationAppDir, List<Resource> list, boolean executable)
+    {
+        String[] rsrcs = config.getMultiValue(name);
+        if (rsrcs == null) {
+            return;
+        }
+        for (String resourcePath : rsrcs) {
+            try {
+                list.add(new Resource(resourcePath, null, new File(applicationAppDir, resourcePath), executable? Resource.EXEC : Resource.NORMAL));
+                log.info("##### Adding resource to list", resourcePath);
+                //System.out.println("##### Adding resource to list"+ resourcePath);
+            } catch (Exception e) {
+                log.warning("Invalid resource '" + resourcePath + "'. " + e);
+                //System.out.println("##### Invalid resource "+ resourcePath);
+            }
+        }
+    }
+    
     protected final EnvConfig _envc;
     protected File _config;
     protected File _backupConfig;
index bc8d140..c654638 100644 (file)
@@ -150,7 +150,7 @@ public class Digest
         // parse and validate our digest file contents
         String filename = digestFile(version);
         StringBuilder data = new StringBuilder();
-        File dfile = new File(appdir, filename);
+        dfile = new File(appdir, filename);
         Config.ParseOpts opts = Config.createOpts(false);
         opts.strictComments = strictComments;
         // bias = toward key: the key is the filename and could conceivably contain = signs, value
@@ -213,6 +213,10 @@ public class Digest
     {
         return _digests.get(resource.getPath());
     }
+    
+    public File getFile() {
+      return dfile;
+    }
 
     /** Used by {@link #createDigest} and {@link Digest}. */
     protected static void note (StringBuilder data, String path, String digest)
@@ -225,4 +229,5 @@ public class Digest
 
     protected static final String FILE_NAME = "digest";
     protected static final String FILE_SUFFIX = ".txt";
+    protected File dfile = null;
 }
index 862c18e..6f99851 100644 (file)
@@ -14,8 +14,10 @@ import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.*;
 
+import com.threerings.getdown.util.LaunchUtil;
 import com.threerings.getdown.util.StringUtil;
 
+import jalview.util.ChannelProperties;
 import jalview.util.HttpUtils;
 
 import com.threerings.getdown.data.Application;
@@ -56,10 +58,15 @@ public final class EnvConfig {
      * configuration source.
      */
     public static EnvConfig create (String[] argv, List<Note> notes) {
+        System.out.println("##### out Starting EnvConfig.create()");
+        System.err.println("##### err Starting EnvConfig.create()");
         String appDir = null, appDirProv = null;
         String appId = null, appIdProv = null;
         String appBase = null, appBaseProv = null;
-
+        applicationFolder = System.getProperty("installer.application_folder");
+        installerAppdir = System.getProperty("installer.appdir");
+        appName = System.getProperty("channel.app_name");
+        
         // start with bootstrap.properties config, if avaialble
         try {
             ResourceBundle bundle = ResourceBundle.getBundle("bootstrap");
@@ -123,7 +130,7 @@ public final class EnvConfig {
             }
         }
 
-        // finally obtain config from command line arguments
+        // not quite finally obtain config from command line arguments
         String argvAppDir = argv.length > 0 ? argv[0] : null;
         if (!StringUtil.isBlank(argvAppDir)) {
             if (appDir == null) {
@@ -145,6 +152,14 @@ public final class EnvConfig {
             }
         }
         
+        boolean userAppDir = false;
+        // platform user appdir addition
+        if (StringUtil.isBlank(appDir)) {
+          appDir = getUserAppdir();
+          appDirProv = "user default";
+          userAppDir = true;
+        }
+        
         int skipArgs = 2;
         // Look for locator file, pass to Application and remove from appArgs
         String argvLocatorFilename = argv.length > 2 ? argv[2] : null;
@@ -163,24 +178,35 @@ public final class EnvConfig {
         }
 
         notes.add(Note.info("Using appdir from " + appDirProv + ": " + appDir));
+        System.out.println(Note.info("##### Using appdir from " + appDirProv + ": " + appDir));
         if (appId != null) notes.add(Note.info("Using appid from " + appIdProv + ": " + appId));
-        if (appBase != null) notes.add(
+        if (appBase != null) {
+          notes.add(
             Note.info("Using appbase from " + appBaseProv + ": " + appBase));
+            System.out.println("##### Using appbase from " + appBaseProv + ": " + appBase);
+        }
 
         // ensure that the appdir refers to a directory that exists
         File appDirFile = new File(appDir);
         if (!appDirFile.exists()) {
+            System.out.println("##### appDir '"+appDir+"' doesn't exist");
             // if we have a bootstrap URL then we auto-create the app dir; this enables an
             // installer to simply place a getdown.jar file somewhere and create an OS shortcut
             // that runs getdown with an appdir and appbase specified, and have getdown create the
             // appdir and download the app into it
+            System.out.println("##### appBase='"+appBase+"'");
             if (!StringUtil.isBlank(appBase)) {
                 if (appDirFile.mkdirs()) {
                     notes.add(Note.info("Auto-created app directory '" + appDir + "'"));
                 } else {
                     notes.add(Note.warn("Unable to auto-create app dir: '" + appDir + "'"));
                 }
+            } else if (userAppDir && Boolean.valueOf(System.getProperty(POPULATE_DEFAULT_APPDIR_PROPERTY))) {
+                System.out.println("##### Created appDir='"+appDir+"'");
+                appBase = System.getProperty(APPLICATION_APPDIR_PROPERTY);
+                Application.copyApplicationAppDirToUserAppDir(System.getProperty(APPLICATION_APPDIR_PROPERTY), appDir);
             } else {
+                System.out.println("##### Invalid appDir='"+appDir+"' and no appBase set");
                 notes.add(Note.error("Invalid appdir '" + appDir + "': directory does not exist"));
                 return null;
             }
@@ -242,6 +268,42 @@ public final class EnvConfig {
         this.certs = certs;
         this.appArgs = appArgs;
     }
+    
+    private static final String getUserAppdir() {
+      System.out.println("##### Property '" + USE_DEFAULT_APPDIR_PROPERTY + "' is '" + System.getProperty(USE_DEFAULT_APPDIR_PROPERTY) + "'");
+      if (!Boolean.valueOf(System.getProperty(USE_DEFAULT_APPDIR_PROPERTY))) {
+        System.out.println("##### NOT setting default app dir");
+        return null;
+      }
+      String appdirname = installerAppdir == null || installerAppdir.length() == 0 ? ChannelProperties.FALLBACK_APPNAME : installerAppdir;
+      String userAppDataPath;
+      String home = System.getProperty("user.home");
+      String appname = StringUtil.isBlank(appName) ? ChannelProperties.FALLBACK_APPNAME : appName;
+      final String FS = File.separator;
+      if (LaunchUtil.isMacOS()) {
+        userAppDataPath = home + FS + "Library" + FS + "Application Support" + FS + "Jalview-Desktop" + FS + appname + FS + "app";
+      } else if (LaunchUtil.isWindows()) {
+        userAppDataPath = home + FS + "AppData" + FS + "Local" + FS + "Jalview-Desktop" + FS + appdirname + FS + "app";
+      } else if (LaunchUtil.isLinux()) {
+        userAppDataPath = home + FS + ".local" + FS + "share" + FS + "jalview-desktop" + FS + appdirname.toLowerCase(Locale.ROOT) + FS + "app";
+      } else {
+        userAppDataPath = home + FS + ".jalview-desktop" + FS + appdirname.toLowerCase(Locale.ROOT) + FS + "app";
+      }
+      System.out.println("##### About to return userAppDataPath value '"+userAppDataPath+"'");
+      return userAppDataPath;
+    }
 
     private static final String USER_HOME_KEY = "${user.home}";
+    
+    private static String applicationFolder = null;
+    
+    private static String installerAppdir = null;
+    
+    private static String appName = null;
+    
+    private static final String USE_DEFAULT_APPDIR_PROPERTY = "usedefaultappdir";
+    
+    private static final String APPLICATION_APPDIR_PROPERTY = "applicationappdir";
+    
+    private static final String POPULATE_DEFAULT_APPDIR_PROPERTY= "populatedefaultappdir";
 }
index 1faaa28..3b81fbb 100644 (file)
@@ -17,6 +17,7 @@ import javax.xml.bind.DatatypeConverter;
 
 import java.security.MessageDigest;
 
+import jalview.util.ChannelProperties;
 import jalview.util.LaunchUtils;
 
 import static com.threerings.getdown.Log.log;
@@ -273,12 +274,12 @@ public class LaunchUtil
     protected static String checkJVMPath (String vmhome, boolean windebug)
     {
         String vmbase = vmhome + File.separator + "bin" + File.separator;
-        String appName = System.getProperty("channel.app_name", "Jalview");
+        String appName = System.getProperty("channel.app_name", ChannelProperties.FALLBACK_APPNAME);
         String vmpath = vmbase + appName;
         if (checkJVMSymlink(vmpath)) {
           return vmpath;
         }
-        vmpath = vmbase + "Jalview";
+        vmpath = vmbase + ChannelProperties.FALLBACK_APPNAME;
         if (checkJVMSymlink(vmpath)) {
           return vmpath;
         }
index ffcb6a1..cf7da8c 100644 (file)
@@ -51,12 +51,14 @@ public class ChannelProperties
 
   private static final ArrayList<Image> iconList;
 
+  public static final String FALLBACK_APPNAME = "Jalview";
+
   static
   {
     defaultProps = new Properties();
     // these should be kept up to date, but in real life they should never
     // actually be used anyway.
-    defaultProps.put("app_name", "Jalview");
+    defaultProps.put("app_name", FALLBACK_APPNAME);
     defaultProps.put("banner", "/default_images/jalview_banner.png");
     defaultProps.put("logo.16", "/default_images/jalview_logo-16.png");
     defaultProps.put("logo.32", "/default_images/jalview_logo-32.png");
index 5894fec..f8cc269 100644 (file)
@@ -230,7 +230,7 @@ public class LaunchUtils
       // appName and "Jalview" will not point to javaw.exe or java.exe but in
       // this case that's okay because the taskbar display name problem doesn't
       // manifest in Windows. See JAL-3820, JAL-4189.
-      for (String name : new String[] { appName, "Jalview", java, javaExe })
+      for (String name : new String[] { appName, ChannelProperties.FALLBACK_APPNAME, java, javaExe })
       {
         if (LaunchUtils.checkJVMSymlink(javaBinDir + name, winConsole))
         {
index dbc50db..87ed8d2 100644 (file)
@@ -116,7 +116,7 @@ public class GetdownApp
     log.info("-- Java Vers: " + System.getProperty("java.version"));
     log.info("-- Java Home: " + System.getProperty("java.home"));
     log.info("-- Install4j Vers: " + Application.i4jVersion);
-    log.info("-- Install4j Template Vers: " + System.getProperty("installer_template_version"));
+    log.info("-- Install4j Template Vers: " + System.getProperty("installer.template_version"));
     log.info("-- User Name: " + System.getProperty("user.name"));
     log.info("-- User Home: " + System.getProperty("user.home"));
     log.info("-- Cur dir: " + System.getProperty("user.dir"));
index a144c83..70da717 100644 (file)
@@ -282,7 +282,7 @@ public final class StatusPanel extends JComponent
                labelText.append("\n");
                labelText.append("install4j version: " + Application.i4jVersion);
                labelText.append("\n");
-               labelText.append("installer version: " + System.getProperty("installer_template_version"));
+               labelText.append("installer version: " + System.getProperty("installer.template_version"));
                labelText.append("\n");
        }
        if (_ifc.displayAppbase) {
index 8c9e44b..6c20166 100644 (file)
Binary files a/j11lib/getdown-core.jar and b/j11lib/getdown-core.jar differ
index 8c9e44b..6c20166 100644 (file)
Binary files a/j8lib/getdown-core.jar and b/j8lib/getdown-core.jar differ