Merge branch 'releases/Release_2_11_3_Branch'
[jalview.git] / src / jalview / bin / Launcher.java
index 6e820fd..2a78d8e 100644 (file)
@@ -28,6 +28,7 @@ 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.LaunchUtils;
 
@@ -35,12 +36,12 @@ import jalview.util.LaunchUtils;
  * A Launcher class for Jalview. This class is used to launch Jalview from the
  * shadowJar when Getdown is not used or available. It attempts to take all the
  * command line arguments to pass on to the jalview.bin.Jalview class, but to
- * insert a -Xmx memory setting to a sensible default, using the -jvmmempc and
+ * insert a -Xmx memory setting to a sensible default, using the --jvmmempc and
  * -jvmmemmax application arguments if specified. If not specified then system
  * properties will be looked for by jalview.bin.MemorySetting. If the user has
- * provided the JVM with a -Xmx setting directly and not set -jvmmempc or
- * -jvmmemmax then this setting will be used and system properties ignored. If
- * -Xmx is set as well as -jvmmempc or -jvmmemmax as argument(s) then the -Xmx
+ * provided the JVM with a -Xmx setting directly and not set --jvmmempc or
+ * --jvmmemmax then this setting will be used and system properties ignored. If
+ * -Xmx is set as well as --jvmmempc or --jvmmemmax as argument(s) then the -Xmx
  * argument will NOT be passed on to the main application launch.
  * 
  * @author bsoares
@@ -50,33 +51,20 @@ public class Launcher
 {
   private final static String startClass = "jalview.bin.Jalview";
 
-  private static boolean checkJVMSymlink(String testBin)
-  {
-    File testBinFile = new File(testBin);
-    if (!testBinFile.exists())
-    {
-      return false;
-    }
-    File targetFile = null;
-    try
-    {
-      targetFile = testBinFile.getCanonicalFile();
-    } catch (IOException e)
-    {
-      return false;
-    }
-    if (targetFile != null && ("java".equals(targetFile.getName())
-            || "java.exe".equals(targetFile.getName())))
-    {
-      return true;
-    }
-    return false;
-  }
+  // not setting this yet due to problem with Jmol
+  private final static String headlessProperty = "java.awt.headless";
+
+  // used for headless macOS
+  private final static String macosHeadlessProperty = "apple.awt.UIElement";
+
+  // arguments that assume headless mode
+  private final static String[] assumeHeadlessArgs = { "headless", "output",
+      "image", "structureimage" };
 
   /**
    * main method for jalview.bin.Launcher. This restarts the same JRE's JVM with
-   * the same arguments but with memory adjusted based on extracted -jvmmempc
-   * and -jvmmemmax application arguments. If on a Mac then extra dock:icon and
+   * the same arguments but with memory adjusted based on extracted --jvmmempc
+   * and --jvmmemmax application arguments. If on a Mac then extra dock:icon and
    * dock:name arguments are also set.
    * 
    * @param args
@@ -85,48 +73,22 @@ public class Launcher
   {
     if (!LaunchUtils.checkJavaVersion())
     {
-      System.err.println("WARNING - The Java version being used (Java "
-              + LaunchUtils.getJavaVersion()
-              + ") may lead to problems. This installation of Jalview should be used with Java "
-              + LaunchUtils.getJavaCompileVersion() + ".");
-    }
-    final String appName = ChannelProperties.getProperty("app_name");
-    final String javaBinDir = System.getProperty("java.home")
-            + File.separator + "bin" + File.separator;
-    String javaBin = null;
-    if (javaBin == null && checkJVMSymlink(javaBinDir + appName))
-    {
-      javaBin = javaBinDir + appName;
-    }
-    if (javaBin == null && checkJVMSymlink(javaBinDir + "Jalview"))
-    {
-      javaBin = javaBinDir + "Jalview";
-    }
-    if (javaBin == null)
-    {
-      javaBin = "java";
-    }
-
-    List<String> command = new ArrayList<>();
-    command.add(javaBin);
-
-    String memSetting = null;
-
-    boolean isAMac = System.getProperty("os.name").indexOf("Mac") > -1;
-
-    for (String jvmArg : ManagementFactory.getRuntimeMXBean()
-            .getInputArguments())
-    {
-      command.add(jvmArg);
+      jalview.bin.Console
+              .errPrintln("WARNING - The Java version being used (Java "
+                      + LaunchUtils.getJavaVersion()
+                      + ") may lead to problems. This installation of Jalview should be used with Java "
+                      + LaunchUtils.getJavaCompileVersion() + ".");
     }
-    command.add("-cp");
-    command.add(ManagementFactory.getRuntimeMXBean().getClassPath());
 
     String jvmmempc = null;
     String jvmmemmax = null;
     boolean debug = false;
     boolean wait = true;
     boolean quiet = false;
+    boolean headless = false;
+    boolean assumeheadless = false;
+    boolean gui = false;
+    boolean help = false;
     boolean stdout = false;
     // must set --debug before --launcher...
     boolean launcherstop = false;
@@ -134,37 +96,70 @@ public class Launcher
     boolean launcherwait = false;
     ArrayList<String> arguments = new ArrayList<>();
     String previousArg = null;
+    // set debug first
     for (String arg : args)
     {
       if (arg.equals("--debug"))
       {
         debug = true;
       }
+    }
+    for (String arg : args)
+    {
       if (arg.equals("--quiet"))
       {
         quiet = true;
       }
-      if (arg.equals("--output=-")
-              || (arg.equals("-") && "--output".equals(previousArg)))
+      else if (arg.equals("--gui"))
       {
-        stdout = true;
+        gui = true;
       }
-      if (debug && arg.equals("--launcherprint"))
+      else if (arg.equals("--help"))
       {
-        launcherprint = true;
+        help = true;
       }
-      if (debug && arg.equals("--launcherstop"))
+      else if (arg.equals("--version"))
       {
-        launcherstop = true;
+        help = true;
+      }
+
+      if (!assumeheadless)
+      {
+        for (String a : assumeHeadlessArgs)
+        {
+          if (arg.equals("--" + a) || arg.startsWith("--" + a + "="))
+          {
+            assumeheadless = true;
+          }
+        }
       }
-      if (debug && arg.equals("--launcherwait"))
+
+      if (arg.equals("--output=-")
+              || (arg.equals("-") && "--output".equals(previousArg)))
       {
-        launcherwait = true;
+        stdout = true;
       }
-      // this ends the launcher immediately
-      if (debug && arg.equals("--launchernowait"))
+
+      if (debug)
       {
-        wait = false;
+        if (arg.equals("--launcherprint"))
+        {
+          launcherprint = true;
+        }
+        else if (arg.equals("--launcherstop"))
+        {
+          launcherstop = true;
+        }
+        else if (arg.equals("--launcherwait"))
+        {
+          launcherwait = true;
+        }
+        else
+        // this ends the launcher immediately
+        if (arg.equals("--launchernowait"))
+        {
+          wait = false;
+        }
       }
       previousArg = arg;
       // Don't add the --launcher... args to Jalview launch
@@ -174,32 +169,24 @@ public class Launcher
       }
       // jvmmempc and jvmmemmax args used to set memory and are not passed on to
       // startClass
-      if (arg.startsWith(
-              "-" + MemorySetting.MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "="))
+      final String jvmmempcArg = Arg.JVMMEMPC.getName();
+      final String jvmmemmaxArg = Arg.JVMMEMMAX.getName();
+      if (arg.startsWith("-" + jvmmempcArg + "="))
       {
-        jvmmempc = arg.substring(
-                MemorySetting.MAX_HEAPSIZE_PERCENT_PROPERTY_NAME.length()
-                        + 2);
+        jvmmempc = arg.substring(jvmmempcArg.length() + 2);
       }
-      else if (arg.startsWith(
-              "-" + MemorySetting.MAX_HEAPSIZE_PROPERTY_NAME + "="))
+      else if (arg.startsWith("-" + jvmmemmaxArg + "="))
       {
-        jvmmemmax = arg.substring(
-                MemorySetting.MAX_HEAPSIZE_PROPERTY_NAME.length() + 2);
+        jvmmemmax = arg.substring(jvmmemmaxArg.length() + 2);
       }
       // --doubledash versions
-      else if (arg.startsWith("--"
-              + MemorySetting.MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "="))
+      else if (arg.startsWith("--" + jvmmempcArg + "="))
       {
-        jvmmempc = arg.substring(
-                MemorySetting.MAX_HEAPSIZE_PERCENT_PROPERTY_NAME.length()
-                        + 3);
+        jvmmempc = arg.substring(jvmmempcArg.length() + 3);
       }
-      else if (arg.startsWith(
-              "--" + MemorySetting.MAX_HEAPSIZE_PROPERTY_NAME + "="))
+      else if (arg.startsWith("--" + jvmmemmaxArg + "="))
       {
-        jvmmemmax = arg.substring(
-                MemorySetting.MAX_HEAPSIZE_PROPERTY_NAME.length() + 3);
+        jvmmemmax = arg.substring(jvmmemmaxArg.length() + 3);
       }
       // retain arg
       else
@@ -207,6 +194,39 @@ public class Launcher
         arguments.add(arg);
       }
     }
+    if (help)
+    {
+      // --help takes precedence over --gui
+      headless = true;
+    }
+    else if (gui)
+    {
+      // --gui takes precedence over --headless
+      headless = false;
+    }
+    else
+    {
+      // --output arguments assume headless mode
+      headless = assumeheadless;
+    }
+
+    final String appName = ChannelProperties.getProperty("app_name");
+
+    // if we're using jalview.bin.Launcher we always assume a console is in use
+    final String javaBin = LaunchUtils.findJavaBin(true);
+
+    List<String> command = 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());
 
     // use saved preferences if no cmdline args
     boolean useCustomisedSettings = LaunchUtils
@@ -229,6 +249,8 @@ public class Launcher
     boolean memSet = false;
     boolean dockIcon = false;
     boolean dockName = false;
+    boolean headlessProp = false;
+    boolean macosHeadlessProp = false;
     for (int i = 0; i < command.size(); i++)
     {
       String arg = command.get(i);
@@ -249,6 +271,14 @@ public class Launcher
       {
         dockName = true;
       }
+      else if (arg.startsWith("-D" + headlessProperty + "="))
+      {
+        headlessProp = true;
+      }
+      else if (arg.startsWith("-D" + macosHeadlessProperty + "="))
+      {
+        macosHeadlessProp = true;
+      }
     }
 
     if (!memSet)
@@ -263,7 +293,7 @@ public class Launcher
       }
     }
 
-    if (isAMac)
+    if (LaunchUtils.isMac)
     {
       if (!dockIcon)
       {
@@ -276,14 +306,25 @@ public class Launcher
         // -Xdock:name=... doesn't actually work :(
         // Leaving it in in case it gets fixed
         command.add("-Xdock:name=" + appName);
-        // this launches WITHOUT an icon in the macOS dock. Could be useful for
-        // getdown?
-        // command.add("-Dapple.awt.UIElement=false");
         // This also does not work for the dock
         command.add("-Dcom.apple.mrj.application.apple.menu.about.name="
                 + appName);
       }
     }
+    if (headless && !headlessProp)
+    {
+      System.setProperty(headlessProperty, "true");
+      /* not setting this in java invocation of running jalview due to problem with Jmol */
+      if (help)
+      {
+        command.add("-D" + headlessProperty + "=true");
+      }
+    }
+    if (headless && LaunchUtils.isMac && !macosHeadlessProp)
+    {
+      System.setProperty(macosHeadlessProperty, "true");
+      command.add("-D" + macosHeadlessProperty + "=true");
+    }
 
     String scalePropertyArg = HiDPISetting.getScalePropertyArg();
     if (scalePropertyArg != null)
@@ -337,7 +378,8 @@ public class Launcher
     {
       if (e.getMessage().toLowerCase(Locale.ROOT).contains("memory"))
       {
-        System.err.println("Caught a memory exception: " + e.getMessage());
+        jalview.bin.Console
+                .errPrintln("Caught a memory exception: " + e.getMessage());
         // Probably the "Cannot allocate memory" error, try without the memory
         // setting
         ArrayList<String> commandNoMem = new ArrayList<>();
@@ -350,7 +392,7 @@ public class Launcher
         }
         final ProcessBuilder builderNoMem = new ProcessBuilder(
                 commandNoMem);
-        System.err.println("Command without memory setting: "
+        jalview.bin.Console.errPrintln("Command without memory setting: "
                 + String.join(" ", builderNoMem.command()));
         try
         {
@@ -376,7 +418,7 @@ public class Launcher
   {
     if (debug && !quiet)
     {
-      System.err.println("LAUNCHERDEBUG - " + message);
+      jalview.bin.Console.errPrintln("LAUNCHERDEBUG - " + message);
     }
   }