JAL-3633 refactored/simplified LaunchUtils code
[jalview.git] / src / jalview / bin / Launcher.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.bin;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.lang.management.ManagementFactory;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import jalview.util.ChannelProperties;
30 import jalview.util.LaunchUtils;
31
32 /**
33  * A Launcher class for Jalview. This class is used to launch Jalview from the
34  * shadowJar when Getdown is not used or available. It attempts to take all the
35  * command line arguments to pass on to the jalview.bin.Jalview class, but to
36  * insert a -Xmx memory setting to a sensible default, using the -jvmmempc and
37  * -jvmmemmax application arguments if specified. If not specified then system
38  * properties will be looked for by jalview.bin.MemorySetting. If the user has
39  * provided the JVM with a -Xmx setting directly and not set -jvmmempc or
40  * -jvmmemmax then this setting will be used and system properties ignored. If
41  * -Xmx is set as well as -jvmmempc or -jvmmemmax as argument(s) then the -Xmx
42  * argument will NOT be passed on to the main application launch.
43  * 
44  * @author bsoares
45  *
46  */
47 public class Launcher
48 {
49   private final static String startClass = "jalview.bin.Jalview";
50
51   private final static String dockIconPath = ChannelProperties
52           .getProperty("logo.512");
53
54   /**
55    * main method for jalview.bin.Launcher. This restarts the same JRE's JVM with
56    * the same arguments but with memory adjusted based on extracted -jvmmempc
57    * and -jvmmemmax application arguments. If on a Mac then extra dock:icon and
58    * dock:name arguments are also set.
59    * 
60    * @param args
61    */
62   public static void main(String[] args)
63   {
64     final String javaBin = System.getProperty("java.home") + File.separator
65             + "bin" + File.separator + "java";
66
67     List<String> command = new ArrayList<>();
68     command.add(javaBin);
69
70     String memSetting = null;
71
72     boolean isAMac = System.getProperty("os.name").indexOf("Mac") > -1;
73
74     for (String jvmArg : ManagementFactory.getRuntimeMXBean()
75             .getInputArguments())
76     {
77       command.add(jvmArg);
78     }
79     command.add("-cp");
80     command.add(ManagementFactory.getRuntimeMXBean().getClassPath());
81
82     String jvmmempc = null;
83     String jvmmemmax = null;
84     ArrayList<String> arguments = new ArrayList<>();
85     for (String arg : args)
86     {
87       // jvmmempc and jvmmemmax args used to set memory and are not passed on to
88       // startClass
89       if (arg.startsWith(
90               "-" + MemorySetting.MAX_HEAPSIZE_PERCENT_PROPERTY_NAME + "="))
91       {
92         jvmmempc = arg.substring(
93                 MemorySetting.MAX_HEAPSIZE_PERCENT_PROPERTY_NAME.length()
94                         + 2);
95       }
96       else if (arg.startsWith(
97               "-" + MemorySetting.MAX_HEAPSIZE_PROPERTY_NAME + "="))
98       {
99         jvmmemmax = arg.substring(
100                 MemorySetting.MAX_HEAPSIZE_PROPERTY_NAME.length() + 2);
101       }
102       else
103       {
104         arguments.add(arg);
105       }
106     }
107
108     // use saved preferences if no cmdline args
109     boolean useCustomisedSettings = LaunchUtils
110             .getBooleanUserPreference(MemorySetting.CUSTOMISED_SETTINGS);
111     if (useCustomisedSettings)
112     {
113       if (jvmmempc == null)
114       {
115         jvmmempc = LaunchUtils
116                 .getUserPreference(MemorySetting.MEMORY_JVMMEMPC);
117       }
118       if (jvmmemmax == null)
119       {
120         jvmmemmax = LaunchUtils
121                 .getUserPreference(MemorySetting.MEMORY_JVMMEMMAX);
122       }
123     }
124
125     // add memory setting if not specified
126     boolean memSet = false;
127     boolean dockIcon = false;
128     boolean dockName = false;
129     for (int i = 0; i < command.size(); i++)
130     {
131       String arg = command.get(i);
132       if (arg.startsWith("-Xmx"))
133       {
134         // only use -Xmx if jvmmemmax and jvmmempc have not been set
135         if (jvmmempc == null && jvmmemmax == null)
136         {
137           memSetting = arg;
138           memSet = true;
139         }
140       }
141       else if (arg.startsWith("-Xdock:icon"))
142       {
143         dockIcon = true;
144       }
145       else if (arg.startsWith("-Xdock:name"))
146       {
147         dockName = true;
148       }
149     }
150
151     if (!memSet)
152     {
153       long maxMemLong = MemorySetting.getMemorySetting(jvmmemmax, jvmmempc);
154
155       if (maxMemLong > 0)
156       {
157         memSetting = "-Xmx" + Long.toString(maxMemLong);
158         memSet = true;
159         command.add(memSetting);
160       }
161     }
162
163     if (isAMac)
164     {
165       if (!dockIcon)
166       {
167         command.add("-Xdock:icon=" + dockIconPath);
168       }
169       if (!dockName)
170       {
171         // -Xdock:name=... doesn't actually work :(
172         // Leaving it in in case it gets fixed
173         command.add(
174                 "-Xdock:name=" + ChannelProperties.getProperty("app_name"));
175       }
176     }
177
178     String scalePropertyArg = HiDPISetting.getScalePropertyArg();
179     if (scalePropertyArg != null)
180     {
181       System.out.println("Running " + startClass + " with scale setting "
182               + scalePropertyArg);
183       command.add(scalePropertyArg);
184     }
185
186     command.add(startClass);
187     command.addAll(arguments);
188
189     final ProcessBuilder builder = new ProcessBuilder(command);
190
191     if (Boolean.parseBoolean(System.getProperty("launcherprint", "false")))
192     {
193       System.out.println(
194               "LAUNCHER COMMAND: " + String.join(" ", builder.command()));
195     }
196     System.out.println("Running " + startClass + " with "
197             + (memSetting == null ? "no memory setting"
198                     : ("memory setting " + memSetting)));
199
200     if (Boolean.parseBoolean(System.getProperty("launcherstop", "false")))
201     {
202       System.exit(0);
203     }
204     try
205     {
206       builder.inheritIO();
207       Process process = builder.start();
208       process.waitFor();
209     } catch (IOException e)
210     {
211       if (e.getMessage().toLowerCase().contains("memory"))
212       {
213         System.out.println("Caught a memory exception: " + e.getMessage());
214         // Probably the "Cannot allocate memory" error, try without the memory
215         // setting
216         ArrayList<String> commandNoMem = new ArrayList<>();
217         for (int i = 0; i < command.size(); i++)
218         {
219           if (!command.get(i).startsWith("-Xmx"))
220           {
221             commandNoMem.add(command.get(i));
222           }
223         }
224         final ProcessBuilder builderNoMem = new ProcessBuilder(
225                 commandNoMem);
226         System.out.println("Command without memory setting: "
227                 + String.join(" ", builderNoMem.command()));
228         try
229         {
230           builderNoMem.inheritIO();
231           Process processNoMem = builderNoMem.start();
232           processNoMem.waitFor();
233         } catch (Exception ex)
234         {
235           ex.printStackTrace();
236         }
237       }
238       else
239       {
240         e.printStackTrace();
241       }
242     } catch (Exception e)
243     {
244       e.printStackTrace();
245     }
246     // System.exit(0);
247   }
248
249 }