From: Ben Soares Date: Tue, 18 Jun 2024 20:36:44 +0000 (+0100) Subject: JAL-3631 Substantial additions to Getdown (through jalview.util.LaunchUtils) to re... X-Git-Tag: Release_2_11_4_0~24^2~33 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=d2145b21b3be14def63b9623953eb7be88fe3fd1;p=jalview.git JAL-3631 Substantial additions to Getdown (through jalview.util.LaunchUtils) to re-launch getdown-launcher.jar when the user's version differs from the installed version. install4j vmoptions file allows administrator to prevent any automatic updates. --- diff --git a/getdown/lib/getdown-core.jar b/getdown/lib/getdown-core.jar index 88464c3..746765e 100644 Binary files a/getdown/lib/getdown-core.jar and b/getdown/lib/getdown-core.jar differ diff --git a/getdown/lib/getdown-launcher-local.jar b/getdown/lib/getdown-launcher-local.jar index 167455d..a913f9b 100644 Binary files a/getdown/lib/getdown-launcher-local.jar and b/getdown/lib/getdown-launcher-local.jar differ diff --git a/getdown/lib/getdown-launcher.jar b/getdown/lib/getdown-launcher.jar index d4fc05d..2414291 100644 Binary files a/getdown/lib/getdown-launcher.jar and b/getdown/lib/getdown-launcher.jar differ diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java index cb51ed6..d5a3def 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java @@ -2068,9 +2068,6 @@ public class Application } 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; @@ -2130,20 +2127,19 @@ public class Application log.warning("Problem opening application digest files", new File(applicationAppDir, Digest.digestFile(Digest.VERSION))); return false; } - // copy getdown.txt, digest.txt and digest2.txt, getdown-launcher.jar + // copy getdown.txt, digest.txt and digest2.txt, getdown-launcher.jar, channel.props File getdownLauncher = new File(applicationAppDir, "getdown-launcher.jar"); - for (File from: new File[] {configFile, digest2.getFile(), digest.getFile(), getdownLauncher}) { + for (File from: new File[] {configFile, digest2.getFile(), digest.getFile(), getdownLauncher, new File(applicationAppDir, "channel.props")}) { 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); + log.warning("Couldn't copy config/digest/getdown-launcher/channel 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 { @@ -2152,8 +2148,6 @@ public class Application 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; @@ -2174,7 +2168,6 @@ public class Application 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) { @@ -2198,11 +2191,8 @@ public class Application 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); } } } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java index a10c67b..3780e6c 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java @@ -7,8 +7,10 @@ package com.threerings.getdown.data; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.security.MessageDigest; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; @@ -19,6 +21,7 @@ import com.threerings.getdown.util.StringUtil; import jalview.util.ChannelProperties; import jalview.util.HttpUtils; +import jalview.util.LaunchUtils; import com.threerings.getdown.data.Application; @@ -58,8 +61,10 @@ public final class EnvConfig { * configuration source. */ public static EnvConfig create (String[] argv, List notes) { - System.out.println("##### out Starting EnvConfig.create()"); - System.err.println("##### err Starting EnvConfig.create()"); + return create(argv, notes, null); + } + + public static EnvConfig create (String[] argv, List notes, Class startClass) { String appDir = null; String appDirProv = null; String appId = null; @@ -67,7 +72,8 @@ public final class EnvConfig { String appBase = null; String appBaseProv = null; applicationFolder = System.getProperty("installer.application_folder"); - installerAppdir = System.getProperty("installer.appdir"); + installerAppdir = System.getProperty(APPLICATION_APPDIR_PROPERTY); + appName = System.getProperty("channel.app_name"); // start with bootstrap.properties config, if avaialble @@ -159,12 +165,58 @@ public final class EnvConfig { // platform user appdir addition if (StringUtil.isBlank(appDir)) { appDir = getUserAppdir(); - appDirProv = "user default"; - userAppDir = appDir != null; + if (appDir != null) { + appDirProv = "user default"; + userAppDir = true; + } } - - if (SysProps.noUpdate() && Boolean.valueOf(System.getProperty("no" + USE_DEFAULT_APPDIR_PROPERTY)) && appDir == null) { - appDir = System.getProperty(APPLICATION_APPDIR_PROPERTY); + + // forced by system property to not use user default appdir + if (SysProps.noUpdate() && Boolean.valueOf(System.getProperty("no" + USER_DEFAULT_APPDIR_PROPERTY))) { + appDir = installerAppdir; + appDirProv = "no user default"; + userAppDir = false; + } else if (userAppDir && installerAppdir != null) { + // we're going to try and use a different appdir, check if newer getdown-launcher.jar found there, and restart using that one + final String getdown = "getdown-launcher.jar"; + File appdirGetdown = new File(appDir, getdown); + if (appdirGetdown.exists()) { + File installerGetdown = new File(installerAppdir, getdown); + try { + Resource appdirGetdownResource = new Resource(appdirGetdown.getAbsolutePath(), null, appdirGetdown, Resource.NORMAL); + Resource installerGetdownResource = new Resource(installerGetdown.getAbsolutePath(), null, installerGetdown, Resource.NORMAL); + MessageDigest md = Digest.getMessageDigest(1); + String appdirGetdownDigest = appdirGetdownResource.computeDigest(1, md, null); + String installerGetdownDigest = installerGetdownResource.computeDigest(1, md, null); + if (!appdirGetdownDigest.equals(installerGetdownDigest)) { // not the same file content + // change classpath and restart. A null startClass looks it up from system property "sun.java.command" + String javaBin = LaunchUtils.findJavaBin(false); + List removeClasspath = new ArrayList<>(); + removeClasspath.add(installerGetdown.getAbsolutePath()); + List prependClasspath = new ArrayList<>(); + prependClasspath.add(appdirGetdown.getAbsolutePath()); + // ensure the appdir and appid are also added as command line arguments for backwards compatibility (earlier versions of getdown-launcher.jar) + List removeArgs = new ArrayList<>(); + removeArgs.add(argvAppDir); + removeArgs.add(argvAppId); + List addArgs = new ArrayList<>(); + addArgs.add(appDir); + addArgs.add(appId); + String startClassName = startClass == null ? null : startClass.getName(); + int exitValue = LaunchUtils.startNewJvm(javaBin, null, null, prependClasspath, null, removeClasspath, startClassName, removeArgs, addArgs, Arrays.asList(argv), true, true, false, true, false); + + if (exitValue == 0) { + notes.add(Note.info("Relaunching getdown succeeded. Original getdown exiting now.")); + setRelaunched(true); + return null; + } else { + notes.add(Note.warn("Relaunching getdown did not seem to succeed with exit value " + exitValue + ". Continuing with this getdown process.")); + } + } + } catch (IOException e) { + notes.add(Note.warn("IOException opening '" + appdirGetdown.getAbsolutePath() + "' and '" + installerGetdown.getAbsolutePath() + "' as Resources. Continuing without restart. " + e.getMessage())); + } + } } int skipArgs = 2; @@ -185,23 +237,19 @@ 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( 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 + "'")); @@ -209,11 +257,9 @@ public final class EnvConfig { 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; } @@ -277,34 +323,51 @@ public final class EnvConfig { } private static final String getUserAppdir() { - System.out.println("##### Property '" + USE_DEFAULT_APPDIR_PROPERTY + "' is '" + System.getProperty(USE_DEFAULT_APPDIR_PROPERTY) + "'"); - final String noUseDefaultAppDirProperty = "no" + USE_DEFAULT_APPDIR_PROPERTY; + final String noUseDefaultAppDirProperty = "no" + USER_DEFAULT_APPDIR_PROPERTY; if (Boolean.valueOf(System.getProperty(noUseDefaultAppDirProperty))) { System.err.println("Not using default user appdir because property '" + noUseDefaultAppDirProperty + "' is '" + System.getProperty(noUseDefaultAppDirProperty) + "'"); return null; } - if (!Boolean.valueOf(System.getProperty(USE_DEFAULT_APPDIR_PROPERTY))) { - System.err.println("Not using default user appdir because property '" + USE_DEFAULT_APPDIR_PROPERTY + "' is '" + System.getProperty(USE_DEFAULT_APPDIR_PROPERTY) + "'"); + if (!Boolean.valueOf(System.getProperty(USER_DEFAULT_APPDIR_PROPERTY))) { + System.err.println("Not using default user appdir because property '" + USER_DEFAULT_APPDIR_PROPERTY + "' is '" + System.getProperty(USER_DEFAULT_APPDIR_PROPERTY) + "'"); return null; } - String appdirname = installerAppdir == null || installerAppdir.length() == 0 ? ChannelProperties.FALLBACK_APPNAME : installerAppdir; - String userAppDataPath; + String appdirname = applicationFolder == null || applicationFolder.length() == 0 ? ChannelProperties.FALLBACK_APPNAME : applicationFolder; String home = System.getProperty("user.home"); String appname = StringUtil.isBlank(appName) ? ChannelProperties.FALLBACK_APPNAME : appName; final String FS = File.separator; + String appDataPath; + String append; if (LaunchUtil.isMacOS()) { - userAppDataPath = home + FS + "Library" + FS + "Application Support" + FS + "Jalview-Desktop" + FS + appname + FS + "app"; + appDataPath = osAppDataPathMap.get("macos"); + append = appname; } else if (LaunchUtil.isWindows()) { - userAppDataPath = home + FS + "AppData" + FS + "Local" + FS + "Jalview-Desktop" + FS + appdirname + FS + "app"; + appDataPath = osAppDataPathMap.get("windows"); + append = appdirname; } else if (LaunchUtil.isLinux()) { - userAppDataPath = home + FS + ".local" + FS + "share" + FS + "jalview-desktop" + FS + appdirname.toLowerCase(Locale.ROOT) + FS + "app"; + appDataPath = osAppDataPathMap.get("linux"); + append = appdirname.toLowerCase(Locale.ROOT); } else { - userAppDataPath = home + FS + ".jalview-desktop" + FS + appdirname.toLowerCase(Locale.ROOT) + FS + "app"; + appDataPath = osAppDataPathMap.get("other"); + append = appdirname.toLowerCase(Locale.ROOT); + } + if (!"/".equals(FS)) { + appDataPath = appDataPath.replaceAll("/", FS); } - System.out.println("##### About to return userAppDataPath value '"+userAppDataPath+"'"); - return userAppDataPath; + String returnString = home + FS + appDataPath + FS + append + FS + "app"; + return returnString; + } + + public static void setRelaunched(boolean b) { + relaunched = b; + } + + public static boolean getRelaunched() { + return relaunched; } + private static boolean relaunched = false; + private static final String USER_HOME_KEY = "${user.home}"; private static String applicationFolder = null; @@ -313,9 +376,20 @@ public final class EnvConfig { private static String appName = null; - private static final String USE_DEFAULT_APPDIR_PROPERTY = "usedefaultappdir"; + private static final String USER_DEFAULT_APPDIR_PROPERTY = "userdefaultappdir"; private static final String APPLICATION_APPDIR_PROPERTY = "installer.appdir"; private static final String POPULATE_DEFAULT_APPDIR_PROPERTY= "populatedefaultappdir"; + + private static final Map osAppDataPathMap; + + static { + // paths from user.home with application folder added to end. '/' replaced with file.separator + osAppDataPathMap = new HashMap<>(); + osAppDataPathMap.put("macos", "Library/Application Support/Jaview-Desktop"); + osAppDataPathMap.put("linux", ".local/share/jalview-desktop"); + osAppDataPathMap.put("windows", "AppData/Local/Jalview-Desktop"); + osAppDataPathMap.put("other", ".jalview-desktop"); + } } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java index 3b81fbb..d6ad03d 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java @@ -104,20 +104,20 @@ public class LaunchUtil } // first look in our application directory for an installed VM - final String appDir = isMacOS() ? + final String appJreDir = isMacOS() ? (new File(appdir, LOCAL_JAVA_DIR).getAbsolutePath()) + "/Contents/Home" : new File(appdir, LOCAL_JAVA_DIR).getAbsolutePath(); - String javaBin = LaunchUtils.findJavaBin(appDir, windebug, false); + String javaBin = LaunchUtils.findJavaBin(appJreDir, windebug, true, false); // then fall back to the VM in which we're already running if (javaBin == null) { - javaBin = LaunchUtils.findJavaBin(System.getProperty("java.home"), windebug, false); + javaBin = LaunchUtils.findJavaBin(System.getProperty("java.home"), windebug, true, false); } // then throw up our hands and hope for the best if (javaBin == null) { - javaBin = LaunchUtils.findJavaBin(null, windebug, true); + javaBin = LaunchUtils.findJavaBin(null, windebug, false, true); log.warning("Unable to find java [appdir=" + appdir + ", java.home=" + System.getProperty("java.home") + "]!"); } diff --git a/getdown/src/getdown/core/src/main/java/jalview/bin/Console.java b/getdown/src/getdown/core/src/main/java/jalview/bin/Console.java deleted file mode 100644 index 66fc865..0000000 --- a/getdown/src/getdown/core/src/main/java/jalview/bin/Console.java +++ /dev/null @@ -1,11 +0,0 @@ -package jalview.bin; - -public class Console { - public static boolean initLogger() { - return false; - } - public static void outPrintln(String s) { - } - public static void errPrintln(String s) { - } -} diff --git a/getdown/src/getdown/core/src/main/java/jalview/util/ErrorLog.java b/getdown/src/getdown/core/src/main/java/jalview/util/ErrorLog.java index e94b59e..bbc758b 100644 --- a/getdown/src/getdown/core/src/main/java/jalview/util/ErrorLog.java +++ b/getdown/src/getdown/core/src/main/java/jalview/util/ErrorLog.java @@ -1,9 +1,32 @@ package jalview.util; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + public class ErrorLog { private static boolean hasConsole = true; + private static Class console = null; + + private static Method initLogger = null; + + private static Method errPrintln = null; + + private static Method outPrintln = null; + + private static String prefix = null; + + public static void setHasConsole(boolean b) + { + hasConsole = b; + } + + public static void setPrefix(String s) + { + prefix = s; + } + public static void outPrintln(String message) { println(message, false); @@ -16,22 +39,64 @@ public class ErrorLog public static void println(String message, boolean err) { - if (hasConsole) + println(message, err, true); + } + + public static void println(String message0, boolean err, + boolean thisHasConsole) + { + String message = prefix == null ? message0 : prefix + message0; + if (thisHasConsole && hasConsole) { try { - hasConsole = jalview.bin.Console.initLogger(); - if (hasConsole) + if (console == null) + { + Class console = Class.forName("jalview.bin.Console"); + } + if (console == null) + { + hasConsole = false; + } + else { - if (err) + if (initLogger == null && console != null) { - jalview.bin.Console.errPrintln(message); + initLogger = console.getMethod("initLogger"); } - else + hasConsole = console == null || initLogger == null + || (Boolean) initLogger.invoke(null); + if (hasConsole && console != null) { - jalview.bin.Console.outPrintln(message); + if (err) + { + if (errPrintln == null) + { + errPrintln = console.getMethod("errPrintln", String.class); + } + errPrintln.invoke(null, message); + } + else + { + if (outPrintln == null) + { + outPrintln = console.getMethod("outPrintln", String.class); + } + outPrintln.invoke(null, message); + } } } + } catch (ClassNotFoundException | NoSuchMethodException e) + { + hasConsole = false; + System.err.println( + "jalview.util.ErrorLog has no jalview.bin.Console.initLogger(). Using System.err and System.out."); + } catch (IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) + { + hasConsole = false; + System.err.println( + "jalview.util.ErrorLog had a problem calling a method of jalview.bin.Console. Using System.err and System.out."); } catch (Exception e) { e.printStackTrace(); @@ -42,7 +107,7 @@ public class ErrorLog "jalview.util.ErrorLog has no jalview.bin.Console. Using System.err and System.out."); } } - if (!hasConsole) + if (!(thisHasConsole && hasConsole)) { if (err) { diff --git a/getdown/src/getdown/core/src/main/java/jalview/util/LaunchUtils.java b/getdown/src/getdown/core/src/main/java/jalview/util/LaunchUtils.java index f8cc269..66c983c 100644 --- a/getdown/src/getdown/core/src/main/java/jalview/util/LaunchUtils.java +++ b/getdown/src/getdown/core/src/main/java/jalview/util/LaunchUtils.java @@ -25,9 +25,18 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.lang.management.ManagementFactory; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; import java.util.Properties; +import java.util.Set; +import java.util.concurrent.TimeUnit; public class LaunchUtils { @@ -112,7 +121,8 @@ public class LaunchUtils null); if (JCV == null) { - ErrorLog.errPrintln("Could not obtain JAVA_COMPILE_VERSION for comparison"); + ErrorLog.errPrintln( + "Could not obtain JAVA_COMPILE_VERSION for comparison"); return -2; } JAVA_COMPILE_VERSION = Integer.parseInt(JCV); @@ -167,6 +177,19 @@ public class LaunchUtils return JAVA_VERSION; } + public static String getJarPath(Class c) + { + try + { + return c.getProtectionDomain().getCodeSource().getLocation().toURI() + .getPath(); + } catch (URISyntaxException e) + { + ErrorLog.errPrintln("Problem with class source location"); + return null; + } + } + public static boolean checkJavaVersion() { if (LaunchUtils.isJS) @@ -197,20 +220,29 @@ public class LaunchUtils public static String findJavaBin(boolean winConsole) { - return findJavaBin(System.getProperty("java.home"), winConsole, true); + return findJavaBin(System.getProperty("java.home"), winConsole, true, + true); + } + + public static String findJavaBin(boolean winConsole, + boolean applicationName, boolean generic) + { + return findJavaBin(System.getProperty("java.home"), winConsole, + applicationName, generic); } /* * Returns a string path to the most likely java binary wanted to run this * installation of Jalview. * + * @param javaHome Try this javaHome dir (defaults to the running java.home). * @param winConsole whether to use java.exe (console) in preference to javaw.exe * (only affects Windows). - * @param javaHome Try this javaHome dir (defaults to the running java.home). + * @param applicationName Look to see if the Jalview application name symbolic link is present and use it. * @param generic Return a generic java command if not found. */ public static String findJavaBin(String javaHome, boolean winConsole, - boolean generic) + boolean applicationName, boolean generic) { String javaBin = null; final String javaExe = winConsole ? "java.exe" : "javaw.exe"; @@ -218,11 +250,16 @@ public class LaunchUtils if (javaHome != null) { + String propertyAppName = null; + String appName = null; // property "channel.app_name" is set by install4j when launching getdown - String propertyAppName = System.getProperty("channel.app_name"); - final String appName = (propertyAppName != null - && propertyAppName.length() > 0) ? propertyAppName - : ChannelProperties.getProperty("app_name"); + if (applicationName) + { + propertyAppName = System.getProperty("channel.app_name"); + appName = (propertyAppName != null && propertyAppName.length() > 0) + ? propertyAppName + : ChannelProperties.getProperty("app_name"); + } final String javaBinDir = javaHome + File.separator + "bin" + File.separator; @@ -230,8 +267,26 @@ 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, ChannelProperties.FALLBACK_APPNAME, java, javaExe }) + List potentialJavaBin = new ArrayList<>(); + if (applicationName) { + if (appName != null) + { + potentialJavaBin.add(appName); + } + if (ChannelProperties.FALLBACK_APPNAME != null) + { + potentialJavaBin.add(ChannelProperties.FALLBACK_APPNAME); + } + } + potentialJavaBin.add(java); + potentialJavaBin.add(javaExe); + for (String name : potentialJavaBin) + { + if (name == null) + { + continue; + } if (LaunchUtils.checkJVMSymlink(javaBinDir + name, winConsole)) { javaBin = javaBinDir + name; @@ -278,4 +333,260 @@ public class LaunchUtils } return false; } + + /** + * Create a java command that matches the currently running java process and + * optionally remove/add some JVM and application parameters. + * + * @param String + * javaBinary The java binary to use. null uses the same as current + * process. + * @param String[] + * removeJvmArguments The (start of) JVM arguments to remove. + * @param String[] + * addJvmArguments JVM arguments to add. + * @param String[] + * prependToClasspath Add these dirs to the start of the classpath + * @param String[] + * appendToClasspath Add these dirs to the end of the classpath + * @param String[] + * deleteFromClasspath Remove these dirs from the existing classpath + * @param String + * startClass The name of the start class if different. null if the + * same. + * @param String[] + * removeAppArguments The (start of) application arguments to remove. + * @param String[] + * addAppArguments Application arguments to add. + * @param boolean + * terminate Flag to terminate this process after starting new + * process. + */ + public static int startNewJvm(String javaBinary, + List removeJvmArguments, List addJvmArguments, + List prependToClasspath, List appendToClasspath, + List removeFromClasspath, String startClass, + List removeAppArguments, List addAppArguments, + List appArguments, boolean launcherprint, + boolean launcherwait, boolean launcherstop, boolean debug, + boolean quiet) + { + int exitValue = -1; + if (javaBinary == null) + { + javaBinary = findJavaBin(false, true, true); + } + + List classpathDirs = new ArrayList<>(); + if (prependToClasspath != null) + { + classpathDirs.addAll(prependToClasspath); + } + + String classpath = ManagementFactory.getRuntimeMXBean().getClassPath(); + if (removeFromClasspath != null) + { + Set removeCp = new HashSet<>(); + for (String dcp : removeFromClasspath) + { + try + { + String canPath = new File(dcp).getCanonicalPath(); + removeCp.add(canPath); + } catch (IOException e) + { + ErrorLog.errPrintln( + "Problem getting canonical path. " + e.getMessage()); + } + } + for (String cp : classpath.split(File.pathSeparator)) + { + try + { + String canPath = new File(cp).getCanonicalPath(); + if (!removeCp.contains(canPath)) + { + classpathDirs.add(cp); + } + } catch (IOException e) + { + ErrorLog.errPrintln( + "Problem getting canonical path. " + e.getMessage()); + } + } + } + else + { + classpathDirs + .addAll(Arrays.asList(classpath.split(File.pathSeparator))); + } + if (appendToClasspath != null) + { + classpathDirs.addAll(appendToClasspath); + } + + List jvmArguments = new ArrayList<>(); + List originalJvmArguments = ManagementFactory.getRuntimeMXBean() + .getInputArguments(); + if (removeJvmArguments != null) + { + for (String jvmArg : originalJvmArguments) + { + boolean addArg = true; + for (String rmArg : removeJvmArguments) + { + if (jvmArg.startsWith(rmArg)) + { + addArg = false; + break; + } + } + if (addArg) + { + jvmArguments.add(jvmArg); + } + } + } + else + { + jvmArguments.addAll(originalJvmArguments); + } + if (addJvmArguments != null) + { + jvmArguments.addAll(addJvmArguments); + } + + if (startClass == null) + { + // this isn't always reliable + startClass = System.getProperty("sun.java.command"); + } + + List applicationArguments = new ArrayList<>(); + if (removeAppArguments != null) + { + Set removeArgs = new HashSet<>(removeAppArguments); + for (String appArg : appArguments) + { + if (!removeArgs.contains(removeArgs)) + { + applicationArguments.add(appArg); + } + } + } + else + { + applicationArguments.addAll(appArguments); + } + if (addAppArguments != null) + { + applicationArguments.addAll(addAppArguments); + } + + List command = new ArrayList<>(); + // java command + command.add(javaBinary); + + // classpath + command.add("-cp"); + command.add(String.join(File.pathSeparator, classpathDirs)); + + // jvm args + command.addAll(jvmArguments); + + // start class + command.add(startClass); + + // application args + command.addAll(applicationArguments); + + final ProcessBuilder builder = new ProcessBuilder(command); + + if (Boolean.parseBoolean(System.getProperty("launcherprint", "false")) + || launcherprint) + { + syserr(debug, quiet, + "COMMAND: " + String.join(" ", builder.command())); + } + + if (Boolean.parseBoolean(System.getProperty("launcherstop", "false")) + || (debug && launcherstop)) + { + syserr(debug, quiet, + "System property 'launcherstop' is set and not 'false'. Exiting."); + System.exit(0); + } + try + { + builder.inheritIO(); + Process process = builder.start(); + if (launcherwait) + { + syserr(debug, quiet, "Launching application process"); + exitValue = process.waitFor(); + syserr(debug, quiet, + "Application process return with value " + exitValue); + } + else + { + int waitInt = 0; + syserr(debug, quiet, + "Wait time for application process is " + waitInt + "ms"); + if (process.waitFor(waitInt, TimeUnit.MILLISECONDS)) + { + exitValue = process.exitValue(); + } + else + { + exitValue = -2; + } + } + syserr(debug, quiet, "Launcher process ending"); + } catch (IOException e) + { + if (e.getMessage().toLowerCase(Locale.ROOT).contains("memory")) + { + syserr(true, quiet, "Caught a memory exception: " + e.getMessage()); + // Probably the "Cannot allocate memory" error, try without the memory + // setting + ArrayList commandNoMem = new ArrayList<>(); + for (int i = 0; i < command.size(); i++) + { + if (!command.get(i).startsWith("-Xmx")) + { + commandNoMem.add(command.get(i)); + } + } + final ProcessBuilder builderNoMem = new ProcessBuilder( + commandNoMem); + syserr(true, quiet, "Command without memory setting: " + + String.join(" ", builderNoMem.command())); + try + { + builderNoMem.inheritIO(); + Process processNoMem = builderNoMem.start(); + exitValue = processNoMem.waitFor(); + } catch (Exception ex) + { + ex.printStackTrace(); + } + } + else + { + e.printStackTrace(); + } + } catch (Exception e) + { + e.printStackTrace(); + } + return exitValue; + } + + public static void syserr(boolean debug, boolean quiet, String message) + { + if (debug && !quiet) + { + ErrorLog.errPrintln("DEBUG - " + message); + } + } } diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java index 200e743..0be4101 100644 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java @@ -1061,6 +1061,8 @@ public abstract class Getdown extends Thread BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String line; while ((line = reader.readLine()) != null) { + out.println(line); + out.flush(); // check for desktop creation line and end early if (!_disposed && line.endsWith("JALVIEW: CREATED DESKTOP")) { // pump the percent up to 100% @@ -1074,8 +1076,6 @@ public abstract class Getdown extends Thread // let nature take its course } } - out.println(line); - out.flush(); } } catch (IOException ioe) { log.warning("Failure copying", "in", in, "out", out, "error", ioe); diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java index 87ed8d2..b52363a 100644 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java @@ -43,6 +43,7 @@ import jalview.util.HttpUtils; public class GetdownApp { public static List startupFilesParameters = new ArrayList<>(); + /** * The main entry point of the Getdown launcher application. */ @@ -61,12 +62,19 @@ public class GetdownApp * @throws Exception if anything goes wrong starting Getdown. */ public static Getdown start (String[] argv) throws Exception { + jalview.util.ErrorLog.setHasConsole(false); + jalview.util.ErrorLog.setPrefix("GETDOWN - "); List notes = new ArrayList<>(); - EnvConfig envc = EnvConfig.create(argv, notes); + EnvConfig envc = EnvConfig.create(argv, notes, GetdownApp.class); if (envc == null) { - if (!notes.isEmpty()) for (EnvConfig.Note n : notes) System.err.println(n.message); - else System.err.println("Usage: java -jar getdown.jar [app_dir] [app_id] [app args]"); - System.exit(-1); + if (!notes.isEmpty()) { + for (EnvConfig.Note n : notes) { + System.err.println(n.message); + } + } else { + System.err.println("Usage: java -jar getdown.jar [app_dir] [app_id] [app args]"); + } + System.exit(EnvConfig.getRelaunched() ? 0 : -1); } // pipe our output into a file in the application directory diff --git a/getdown/src/getdown/mvn_cmd b/getdown/src/getdown/mvn_cmd index 59cb761..52b4b40 100755 --- a/getdown/src/getdown/mvn_cmd +++ b/getdown/src/getdown/mvn_cmd @@ -12,6 +12,18 @@ fi echo "Setting VERSION to '$VERSION'" perl -p -i -e 's|()[^<]*JVL[^<]*()|${1}$ENV{VERSION}${2}|;' pom.xml */pom.xml + +echo "Making sure jalview classes are up-to-date" +for x in $(find core/src/main/java/jalview launcher/src/main/java/jalview -name "*.java") +do + y=${x##*java/} + if [ -e "../../../src/${y}" ]; then + echo /bin/cp "../../../src/${y}" "${x}" + /bin/cp "../../../src/${y}" "${x}" + else + echo "'../../../src/${y}' doesn't exist, not copying" + fi +done mvn package -Dgetdown.host.whitelist="jalview.org,*.jalview.org" -Dallow_file_protocol=false -Dconnect_timeout=8 -Dread_timeout=15 RET=$? if [ x$RET = x0 ]; then diff --git a/j11lib/getdown-core.jar b/j11lib/getdown-core.jar index 88464c3..746765e 100644 Binary files a/j11lib/getdown-core.jar and b/j11lib/getdown-core.jar differ diff --git a/j8lib/getdown-core.jar b/j8lib/getdown-core.jar index 88464c3..746765e 100644 Binary files a/j8lib/getdown-core.jar and b/j8lib/getdown-core.jar differ diff --git a/src/jalview/bin/Launcher.java b/src/jalview/bin/Launcher.java index 2a78d8e..9387be4 100644 --- a/src/jalview/bin/Launcher.java +++ b/src/jalview/bin/Launcher.java @@ -21,15 +21,13 @@ package jalview.bin; import java.io.File; -import java.io.IOException; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.List; -import java.util.Locale; -import java.util.concurrent.TimeUnit; import jalview.bin.argparser.Arg; import jalview.util.ChannelProperties; +import jalview.util.ErrorLog; import jalview.util.LaunchUtils; /** @@ -80,6 +78,8 @@ public class Launcher + LaunchUtils.getJavaCompileVersion() + "."); } + ErrorLog.setPrefix("LAUNCHER - "); + String jvmmempc = null; String jvmmemmax = null; boolean debug = false; @@ -215,18 +215,14 @@ public class Launcher // if we're using jalview.bin.Launcher we always assume a console is in use final String javaBin = LaunchUtils.findJavaBin(true); - List command = new ArrayList<>(); - command.add(javaBin); + List removeJvmArgs = new ArrayList<>(); + List addJvmArgs = new ArrayList<>(); + // command.add(javaBin); String memSetting = null; - for (String jvmArg : ManagementFactory.getRuntimeMXBean() - .getInputArguments()) - { - command.add(jvmArg); - } - command.add("-cp"); - command.add(ManagementFactory.getRuntimeMXBean().getClassPath()); + List jvmArgs = ManagementFactory.getRuntimeMXBean() + .getInputArguments(); // use saved preferences if no cmdline args boolean useCustomisedSettings = LaunchUtils @@ -251,9 +247,9 @@ public class Launcher boolean dockName = false; boolean headlessProp = false; boolean macosHeadlessProp = false; - for (int i = 0; i < command.size(); i++) + for (int i = 0; i < jvmArgs.size(); i++) { - String arg = command.get(i); + String arg = jvmArgs.get(i); if (arg.startsWith("-Xmx")) { // only use -Xmx if jvmmemmax and jvmmempc have not been set @@ -289,7 +285,8 @@ public class Launcher { memSetting = "-Xmx" + Long.toString(maxMemLong); memSet = true; - command.add(memSetting); + addJvmArgs.add(memSetting); + removeJvmArgs.add("-Xmx"); } } @@ -299,15 +296,15 @@ public class Launcher { String dockIconPath = System.getProperty("getdownappdir", ".") + File.separator + "resource/jalview_logo.png"; - command.add("-Xdock:icon=" + dockIconPath); + addJvmArgs.add("-Xdock:icon=" + dockIconPath); } if (!dockName) { // -Xdock:name=... doesn't actually work :( // Leaving it in in case it gets fixed - command.add("-Xdock:name=" + appName); + addJvmArgs.add("-Xdock:name=" + appName); // This also does not work for the dock - command.add("-Dcom.apple.mrj.application.apple.menu.about.name=" + addJvmArgs.add("-Dcom.apple.mrj.application.apple.menu.about.name=" + appName); } } @@ -317,109 +314,28 @@ public class Launcher /* not setting this in java invocation of running jalview due to problem with Jmol */ if (help) { - command.add("-D" + headlessProperty + "=true"); + addJvmArgs.add("-D" + headlessProperty + "=true"); } } if (headless && LaunchUtils.isMac && !macosHeadlessProp) { System.setProperty(macosHeadlessProperty, "true"); - command.add("-D" + macosHeadlessProperty + "=true"); + addJvmArgs.add("-D" + macosHeadlessProperty + "=true"); } String scalePropertyArg = HiDPISetting.getScalePropertyArg(); if (scalePropertyArg != null) { - syserr(debug, quiet, "Running " + startClass + " with scale setting " - + scalePropertyArg); - command.add(scalePropertyArg); + LaunchUtils.syserr(debug, quiet, "Running " + startClass + + " with scale setting " + scalePropertyArg); + addJvmArgs.add(scalePropertyArg); } - command.add(startClass); - command.addAll(arguments); + int exitValue = LaunchUtils.startNewJvm(javaBin, removeJvmArgs, + addJvmArgs, null, null, null, startClass, null, null, arguments, + launcherprint, launcherwait, launcherstop, debug, quiet); - final ProcessBuilder builder = new ProcessBuilder(command); - - if ((Boolean.parseBoolean(System.getProperty("launcherprint", "false")) - || launcherprint)) - { - syserr(debug, quiet, - "LAUNCHER COMMAND: " + String.join(" ", builder.command())); - } - syserr(debug, quiet, - "Running " + startClass + " with " - + (memSetting == null ? "no memory setting" - : ("memory setting " + memSetting))); - - if (Boolean.parseBoolean(System.getProperty("launcherstop", "false")) - || (debug && launcherstop)) - { - syserr(debug, quiet, - "System property 'launcherstop' is set and not 'false'. Exiting."); - System.exit(0); - } - try - { - builder.inheritIO(); - Process process = builder.start(); - if (wait || launcherwait) - { - syserr(debug, quiet, "Launching application process"); - process.waitFor(); - } - else - { - int waitInt = 0; - syserr(debug, quiet, - "Wait time for application process is " + waitInt + "ms"); - process.waitFor(waitInt, TimeUnit.MILLISECONDS); - } - syserr(debug, quiet, "Launcher process ending"); - } catch (IOException e) - { - if (e.getMessage().toLowerCase(Locale.ROOT).contains("memory")) - { - jalview.bin.Console - .errPrintln("Caught a memory exception: " + e.getMessage()); - // Probably the "Cannot allocate memory" error, try without the memory - // setting - ArrayList commandNoMem = new ArrayList<>(); - for (int i = 0; i < command.size(); i++) - { - if (!command.get(i).startsWith("-Xmx")) - { - commandNoMem.add(command.get(i)); - } - } - final ProcessBuilder builderNoMem = new ProcessBuilder( - commandNoMem); - jalview.bin.Console.errPrintln("Command without memory setting: " - + String.join(" ", builderNoMem.command())); - try - { - builderNoMem.inheritIO(); - Process processNoMem = builderNoMem.start(); - processNoMem.waitFor(); - } catch (Exception ex) - { - ex.printStackTrace(); - } - } - else - { - e.printStackTrace(); - } - } catch (Exception e) - { - e.printStackTrace(); - } - } - - private static void syserr(boolean debug, boolean quiet, String message) - { - if (debug && !quiet) - { - jalview.bin.Console.errPrintln("LAUNCHERDEBUG - " + message); - } + LaunchUtils.syserr(debug, quiet, "JVM exited with value " + exitValue); } } diff --git a/src/jalview/util/ErrorLog.java b/src/jalview/util/ErrorLog.java index e94b59e..bbc758b 100644 --- a/src/jalview/util/ErrorLog.java +++ b/src/jalview/util/ErrorLog.java @@ -1,9 +1,32 @@ package jalview.util; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + public class ErrorLog { private static boolean hasConsole = true; + private static Class console = null; + + private static Method initLogger = null; + + private static Method errPrintln = null; + + private static Method outPrintln = null; + + private static String prefix = null; + + public static void setHasConsole(boolean b) + { + hasConsole = b; + } + + public static void setPrefix(String s) + { + prefix = s; + } + public static void outPrintln(String message) { println(message, false); @@ -16,22 +39,64 @@ public class ErrorLog public static void println(String message, boolean err) { - if (hasConsole) + println(message, err, true); + } + + public static void println(String message0, boolean err, + boolean thisHasConsole) + { + String message = prefix == null ? message0 : prefix + message0; + if (thisHasConsole && hasConsole) { try { - hasConsole = jalview.bin.Console.initLogger(); - if (hasConsole) + if (console == null) + { + Class console = Class.forName("jalview.bin.Console"); + } + if (console == null) + { + hasConsole = false; + } + else { - if (err) + if (initLogger == null && console != null) { - jalview.bin.Console.errPrintln(message); + initLogger = console.getMethod("initLogger"); } - else + hasConsole = console == null || initLogger == null + || (Boolean) initLogger.invoke(null); + if (hasConsole && console != null) { - jalview.bin.Console.outPrintln(message); + if (err) + { + if (errPrintln == null) + { + errPrintln = console.getMethod("errPrintln", String.class); + } + errPrintln.invoke(null, message); + } + else + { + if (outPrintln == null) + { + outPrintln = console.getMethod("outPrintln", String.class); + } + outPrintln.invoke(null, message); + } } } + } catch (ClassNotFoundException | NoSuchMethodException e) + { + hasConsole = false; + System.err.println( + "jalview.util.ErrorLog has no jalview.bin.Console.initLogger(). Using System.err and System.out."); + } catch (IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) + { + hasConsole = false; + System.err.println( + "jalview.util.ErrorLog had a problem calling a method of jalview.bin.Console. Using System.err and System.out."); } catch (Exception e) { e.printStackTrace(); @@ -42,7 +107,7 @@ public class ErrorLog "jalview.util.ErrorLog has no jalview.bin.Console. Using System.err and System.out."); } } - if (!hasConsole) + if (!(thisHasConsole && hasConsole)) { if (err) { diff --git a/src/jalview/util/LaunchUtils.java b/src/jalview/util/LaunchUtils.java index f8cc269..66c983c 100644 --- a/src/jalview/util/LaunchUtils.java +++ b/src/jalview/util/LaunchUtils.java @@ -25,9 +25,18 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.lang.management.ManagementFactory; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; import java.util.Properties; +import java.util.Set; +import java.util.concurrent.TimeUnit; public class LaunchUtils { @@ -112,7 +121,8 @@ public class LaunchUtils null); if (JCV == null) { - ErrorLog.errPrintln("Could not obtain JAVA_COMPILE_VERSION for comparison"); + ErrorLog.errPrintln( + "Could not obtain JAVA_COMPILE_VERSION for comparison"); return -2; } JAVA_COMPILE_VERSION = Integer.parseInt(JCV); @@ -167,6 +177,19 @@ public class LaunchUtils return JAVA_VERSION; } + public static String getJarPath(Class c) + { + try + { + return c.getProtectionDomain().getCodeSource().getLocation().toURI() + .getPath(); + } catch (URISyntaxException e) + { + ErrorLog.errPrintln("Problem with class source location"); + return null; + } + } + public static boolean checkJavaVersion() { if (LaunchUtils.isJS) @@ -197,20 +220,29 @@ public class LaunchUtils public static String findJavaBin(boolean winConsole) { - return findJavaBin(System.getProperty("java.home"), winConsole, true); + return findJavaBin(System.getProperty("java.home"), winConsole, true, + true); + } + + public static String findJavaBin(boolean winConsole, + boolean applicationName, boolean generic) + { + return findJavaBin(System.getProperty("java.home"), winConsole, + applicationName, generic); } /* * Returns a string path to the most likely java binary wanted to run this * installation of Jalview. * + * @param javaHome Try this javaHome dir (defaults to the running java.home). * @param winConsole whether to use java.exe (console) in preference to javaw.exe * (only affects Windows). - * @param javaHome Try this javaHome dir (defaults to the running java.home). + * @param applicationName Look to see if the Jalview application name symbolic link is present and use it. * @param generic Return a generic java command if not found. */ public static String findJavaBin(String javaHome, boolean winConsole, - boolean generic) + boolean applicationName, boolean generic) { String javaBin = null; final String javaExe = winConsole ? "java.exe" : "javaw.exe"; @@ -218,11 +250,16 @@ public class LaunchUtils if (javaHome != null) { + String propertyAppName = null; + String appName = null; // property "channel.app_name" is set by install4j when launching getdown - String propertyAppName = System.getProperty("channel.app_name"); - final String appName = (propertyAppName != null - && propertyAppName.length() > 0) ? propertyAppName - : ChannelProperties.getProperty("app_name"); + if (applicationName) + { + propertyAppName = System.getProperty("channel.app_name"); + appName = (propertyAppName != null && propertyAppName.length() > 0) + ? propertyAppName + : ChannelProperties.getProperty("app_name"); + } final String javaBinDir = javaHome + File.separator + "bin" + File.separator; @@ -230,8 +267,26 @@ 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, ChannelProperties.FALLBACK_APPNAME, java, javaExe }) + List potentialJavaBin = new ArrayList<>(); + if (applicationName) { + if (appName != null) + { + potentialJavaBin.add(appName); + } + if (ChannelProperties.FALLBACK_APPNAME != null) + { + potentialJavaBin.add(ChannelProperties.FALLBACK_APPNAME); + } + } + potentialJavaBin.add(java); + potentialJavaBin.add(javaExe); + for (String name : potentialJavaBin) + { + if (name == null) + { + continue; + } if (LaunchUtils.checkJVMSymlink(javaBinDir + name, winConsole)) { javaBin = javaBinDir + name; @@ -278,4 +333,260 @@ public class LaunchUtils } return false; } + + /** + * Create a java command that matches the currently running java process and + * optionally remove/add some JVM and application parameters. + * + * @param String + * javaBinary The java binary to use. null uses the same as current + * process. + * @param String[] + * removeJvmArguments The (start of) JVM arguments to remove. + * @param String[] + * addJvmArguments JVM arguments to add. + * @param String[] + * prependToClasspath Add these dirs to the start of the classpath + * @param String[] + * appendToClasspath Add these dirs to the end of the classpath + * @param String[] + * deleteFromClasspath Remove these dirs from the existing classpath + * @param String + * startClass The name of the start class if different. null if the + * same. + * @param String[] + * removeAppArguments The (start of) application arguments to remove. + * @param String[] + * addAppArguments Application arguments to add. + * @param boolean + * terminate Flag to terminate this process after starting new + * process. + */ + public static int startNewJvm(String javaBinary, + List removeJvmArguments, List addJvmArguments, + List prependToClasspath, List appendToClasspath, + List removeFromClasspath, String startClass, + List removeAppArguments, List addAppArguments, + List appArguments, boolean launcherprint, + boolean launcherwait, boolean launcherstop, boolean debug, + boolean quiet) + { + int exitValue = -1; + if (javaBinary == null) + { + javaBinary = findJavaBin(false, true, true); + } + + List classpathDirs = new ArrayList<>(); + if (prependToClasspath != null) + { + classpathDirs.addAll(prependToClasspath); + } + + String classpath = ManagementFactory.getRuntimeMXBean().getClassPath(); + if (removeFromClasspath != null) + { + Set removeCp = new HashSet<>(); + for (String dcp : removeFromClasspath) + { + try + { + String canPath = new File(dcp).getCanonicalPath(); + removeCp.add(canPath); + } catch (IOException e) + { + ErrorLog.errPrintln( + "Problem getting canonical path. " + e.getMessage()); + } + } + for (String cp : classpath.split(File.pathSeparator)) + { + try + { + String canPath = new File(cp).getCanonicalPath(); + if (!removeCp.contains(canPath)) + { + classpathDirs.add(cp); + } + } catch (IOException e) + { + ErrorLog.errPrintln( + "Problem getting canonical path. " + e.getMessage()); + } + } + } + else + { + classpathDirs + .addAll(Arrays.asList(classpath.split(File.pathSeparator))); + } + if (appendToClasspath != null) + { + classpathDirs.addAll(appendToClasspath); + } + + List jvmArguments = new ArrayList<>(); + List originalJvmArguments = ManagementFactory.getRuntimeMXBean() + .getInputArguments(); + if (removeJvmArguments != null) + { + for (String jvmArg : originalJvmArguments) + { + boolean addArg = true; + for (String rmArg : removeJvmArguments) + { + if (jvmArg.startsWith(rmArg)) + { + addArg = false; + break; + } + } + if (addArg) + { + jvmArguments.add(jvmArg); + } + } + } + else + { + jvmArguments.addAll(originalJvmArguments); + } + if (addJvmArguments != null) + { + jvmArguments.addAll(addJvmArguments); + } + + if (startClass == null) + { + // this isn't always reliable + startClass = System.getProperty("sun.java.command"); + } + + List applicationArguments = new ArrayList<>(); + if (removeAppArguments != null) + { + Set removeArgs = new HashSet<>(removeAppArguments); + for (String appArg : appArguments) + { + if (!removeArgs.contains(removeArgs)) + { + applicationArguments.add(appArg); + } + } + } + else + { + applicationArguments.addAll(appArguments); + } + if (addAppArguments != null) + { + applicationArguments.addAll(addAppArguments); + } + + List command = new ArrayList<>(); + // java command + command.add(javaBinary); + + // classpath + command.add("-cp"); + command.add(String.join(File.pathSeparator, classpathDirs)); + + // jvm args + command.addAll(jvmArguments); + + // start class + command.add(startClass); + + // application args + command.addAll(applicationArguments); + + final ProcessBuilder builder = new ProcessBuilder(command); + + if (Boolean.parseBoolean(System.getProperty("launcherprint", "false")) + || launcherprint) + { + syserr(debug, quiet, + "COMMAND: " + String.join(" ", builder.command())); + } + + if (Boolean.parseBoolean(System.getProperty("launcherstop", "false")) + || (debug && launcherstop)) + { + syserr(debug, quiet, + "System property 'launcherstop' is set and not 'false'. Exiting."); + System.exit(0); + } + try + { + builder.inheritIO(); + Process process = builder.start(); + if (launcherwait) + { + syserr(debug, quiet, "Launching application process"); + exitValue = process.waitFor(); + syserr(debug, quiet, + "Application process return with value " + exitValue); + } + else + { + int waitInt = 0; + syserr(debug, quiet, + "Wait time for application process is " + waitInt + "ms"); + if (process.waitFor(waitInt, TimeUnit.MILLISECONDS)) + { + exitValue = process.exitValue(); + } + else + { + exitValue = -2; + } + } + syserr(debug, quiet, "Launcher process ending"); + } catch (IOException e) + { + if (e.getMessage().toLowerCase(Locale.ROOT).contains("memory")) + { + syserr(true, quiet, "Caught a memory exception: " + e.getMessage()); + // Probably the "Cannot allocate memory" error, try without the memory + // setting + ArrayList commandNoMem = new ArrayList<>(); + for (int i = 0; i < command.size(); i++) + { + if (!command.get(i).startsWith("-Xmx")) + { + commandNoMem.add(command.get(i)); + } + } + final ProcessBuilder builderNoMem = new ProcessBuilder( + commandNoMem); + syserr(true, quiet, "Command without memory setting: " + + String.join(" ", builderNoMem.command())); + try + { + builderNoMem.inheritIO(); + Process processNoMem = builderNoMem.start(); + exitValue = processNoMem.waitFor(); + } catch (Exception ex) + { + ex.printStackTrace(); + } + } + else + { + e.printStackTrace(); + } + } catch (Exception e) + { + e.printStackTrace(); + } + return exitValue; + } + + public static void syserr(boolean debug, boolean quiet, String message) + { + if (debug && !quiet) + { + ErrorLog.errPrintln("DEBUG - " + message); + } + } } diff --git a/utils/install4j/install4j10_template.install4j b/utils/install4j/install4j10_template.install4j index 44421c2..e019562 100644 --- a/utils/install4j/install4j10_template.install4j +++ b/utils/install4j/install4j10_template.install4j @@ -1,5 +1,5 @@ - + @@ -138,7 +138,7 @@ - + @@ -147,7 +147,7 @@ - + @@ -165,7 +165,7 @@ # -include-options [path to other .vmoption file] # Uncomment these two lines to disable user-space automatic updates. -#-Dnousedefaultappdir=true +#-Dnouserdefaultappdir=true #-Dsilent=noupdate ${compiler:file("${compiler:INFO_PLIST_FILE_ASSOCIATIONS_FILE}")}