Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.util

File LaunchUtils.java

 

Coverage histogram

../../img/srcFileCovDistChart3.png
52% of files have more coverage

Code metrics

132
258
18
1
751
598
112
0.43
14.33
18
6.22

Classes

Class Line # Actions
LaunchUtils 44 258 112
0.2794117627.9%
 

Contributing tests

This file is covered by 92 tests. .

Source view

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.util;
22   
23    import java.io.File;
24    import java.io.FileInputStream;
25    import java.io.FileNotFoundException;
26    import java.io.IOException;
27    import java.io.InputStream;
28    import java.lang.management.ManagementFactory;
29    import java.net.MalformedURLException;
30    import java.net.URISyntaxException;
31    import java.net.URL;
32    import java.util.ArrayList;
33    import java.util.Arrays;
34    import java.util.HashSet;
35    import java.util.List;
36    import java.util.Locale;
37    import java.util.Properties;
38    import java.util.Set;
39    import java.util.concurrent.TimeUnit;
40    import java.util.jar.Attributes;
41    import java.util.jar.JarInputStream;
42    import java.util.jar.Manifest;
43   
 
44    public class LaunchUtils
45    {
46    // setting these is LaunchUtils so don't need to import Platform
47    public final static boolean isMac = System.getProperty("os.name")
48    .indexOf("Mac") > -1;
49   
50    public final static boolean isWindows = System.getProperty("os.name")
51    .indexOf("Win") > -1;
52   
53    private static boolean isJS = /** @j2sNative true || */
54    false;
55   
56    public static final String LOGFILE_HANDOVER = "LOGFILE_HANDOVER";
57   
 
58  0 toggle public static void loadChannelProps(File dir)
59    {
60  0 ChannelProperties.loadProps(dir);
61    }
62   
63    private static Properties userPreferences = null;
64   
 
65  0 toggle public static String getUserPreference(String key)
66    {
67  0 if (userPreferences == null)
68    {
69  0 String channelPrefsFilename = ChannelProperties
70    .getProperty("preferences.filename");
71  0 if (channelPrefsFilename == null)
72    {
73  0 return null;
74    }
75  0 File propertiesFile = new File(System.getProperty("user.home"),
76    channelPrefsFilename);
77  0 if (!propertiesFile.exists())
78    {
79  0 return null;
80    }
81  0 try
82    {
83  0 userPreferences = new Properties();
84  0 userPreferences.load(new FileInputStream(propertiesFile));
85    } catch (FileNotFoundException e)
86    {
87    // didn't find user preferences file
88  0 return null;
89    } catch (IOException e)
90    {
91  0 ErrorLog.errPrintln(e.getMessage());
92  0 return null;
93    }
94    }
95  0 return userPreferences.getProperty(key);
96    }
97   
 
98  0 toggle public static boolean getBooleanUserPreference(String key)
99    {
100  0 return Boolean.parseBoolean(getUserPreference(key));
101    }
102   
103    public static int JAVA_COMPILE_VERSION = 0;
104   
 
105  104 toggle public static int getJavaCompileVersion()
106    {
107  104 if (LaunchUtils.isJS)
108    {
109  0 return -1;
110    }
111  104 else if (JAVA_COMPILE_VERSION > 0)
112    {
113  0 return JAVA_COMPILE_VERSION;
114    }
115  104 String buildDetails = "jar:".concat(LaunchUtils.class
116    .getProtectionDomain().getCodeSource().getLocation().toString()
117    .concat("!" + "/.build_properties"));
118  104 try
119    {
120  104 URL localFileURL = new URL(buildDetails);
121  104 InputStream in = HttpUtils.openStream(localFileURL);
122  0 Properties buildProperties = new Properties();
123  0 buildProperties.load(in);
124  0 in.close();
125  0 String JCV = buildProperties.getProperty("JAVA_COMPILE_VERSION",
126    null);
127  0 if (JCV == null)
128    {
129  0 ErrorLog.errPrintln(
130    "Could not obtain JAVA_COMPILE_VERSION for comparison");
131  0 return -2;
132    }
133  0 JAVA_COMPILE_VERSION = Integer.parseInt(JCV);
134    } catch (MalformedURLException e)
135    {
136  0 ErrorLog.errPrintln("Could not find " + buildDetails);
137  0 return -3;
138    } catch (IOException e)
139    {
140  104 ErrorLog.errPrintln("Could not load " + buildDetails);
141  104 return -4;
142    } catch (NumberFormatException e)
143    {
144  0 ErrorLog.errPrintln("Could not parse JAVA_COMPILE_VERSION");
145  0 return -5;
146    }
147   
148  0 return JAVA_COMPILE_VERSION;
149    }
150   
151    public static int JAVA_VERSION = 0;
152   
 
153  516 toggle public static int getJavaVersion()
154    {
155  516 if (LaunchUtils.isJS)
156    {
157  0 return -1;
158    }
159  516 else if (JAVA_VERSION > 0)
160    {
161  462 return JAVA_VERSION;
162    }
163  54 try
164    {
165  54 String JV = System.getProperty("java.version");
166  54 if (JV == null)
167    {
168  0 ErrorLog.errPrintln("Could not obtain java.version for comparison");
169  0 return -2;
170    }
171  54 if (JV.startsWith("1."))
172    {
173  0 JV = JV.substring(2);
174    }
175  54 JAVA_VERSION = JV.indexOf(".") == -1 ? Integer.parseInt(JV)
176    : Integer.parseInt(JV.substring(0, JV.indexOf(".")));
177    } catch (NumberFormatException e)
178    {
179  0 ErrorLog.errPrintln("Could not parse java.version");
180  0 return -3;
181    }
182  54 return JAVA_VERSION;
183    }
184   
 
185  0 toggle public static String getJarPath(Class c)
186    {
187  0 try
188    {
189  0 return c.getProtectionDomain().getCodeSource().getLocation().toURI()
190    .getPath();
191    } catch (URISyntaxException e)
192    {
193  0 ErrorLog.errPrintln("Problem with class source location");
194  0 return null;
195    }
196    }
197   
 
198  104 toggle public static boolean checkJavaVersion()
199    {
200  104 if (LaunchUtils.isJS)
201    {
202  0 return true;
203    }
204  104 String buildDetails = "jar:".concat(LaunchUtils.class
205    .getProtectionDomain().getCodeSource().getLocation().toString()
206    .concat("!" + "/.build_properties"));
207   
208  104 int java_compile_version = getJavaCompileVersion();
209  104 int java_version = getJavaVersion();
210   
211  104 if (java_compile_version <= 0 || java_version <= 0)
212    {
213  104 ErrorLog.errPrintln("Could not make Java version check");
214  104 return true;
215    }
216    // Warn if these java.version and JAVA_COMPILE_VERSION conditions exist
217    // Usually this means a Java 11 compiled JAR being run by a Java 11 JVM
218  0 if (java_version >= 11 && java_compile_version < 11)
219    {
220  0 return false;
221    }
222   
223  0 return true;
224    }
225   
 
226  0 toggle public static String findJavaBin(boolean winConsole)
227    {
228  0 return findJavaBin(System.getProperty("java.home"), winConsole, true,
229    true);
230    }
231   
 
232  0 toggle public static String findJavaBin(boolean winConsole,
233    boolean applicationName, boolean generic)
234    {
235  0 return findJavaBin(System.getProperty("java.home"), winConsole,
236    applicationName, generic);
237    }
238   
239    /*
240    * Returns a string path to the most likely java binary wanted to run this
241    * installation of Jalview.
242    *
243    * @param javaHome Try this javaHome dir (defaults to the running java.home).
244    * @param winConsole whether to use java.exe (console) in preference to javaw.exe
245    * (only affects Windows).
246    * @param applicationName Look to see if the Jalview application name symbolic link is present and use it.
247    * @param generic Return a generic java command if not found.
248    */
 
249  0 toggle public static String findJavaBin(String javaHome, boolean winConsole,
250    boolean applicationName, boolean generic)
251    {
252  0 String javaBin = null;
253  0 final String javaExe = winConsole ? "java.exe" : "javaw.exe";
254  0 final String java = "java";
255   
256  0 if (javaHome != null)
257    {
258  0 String propertyAppName = null;
259  0 String appName = null;
260    // property "channel.app_name" is set by install4j when launching getdown
261  0 if (applicationName)
262    {
263  0 propertyAppName = System.getProperty("channel.app_name");
264  0 appName = (propertyAppName != null && propertyAppName.length() > 0)
265    ? propertyAppName
266    : ChannelProperties.getProperty("app_name");
267    }
268   
269  0 final String javaBinDir = javaHome + File.separator + "bin"
270    + File.separator;
271   
272    // appName and "Jalview" will not point to javaw.exe or java.exe but in
273    // this case that's okay because the taskbar display name problem doesn't
274    // manifest in Windows. See JAL-3820, JAL-4189.
275  0 List<String> potentialJavaBin = new ArrayList<>();
276  0 if (applicationName)
277    {
278  0 if (appName != null)
279    {
280  0 potentialJavaBin.add(appName);
281    }
282  0 if (ChannelProperties.FALLBACK_APPNAME != null)
283    {
284  0 potentialJavaBin.add(ChannelProperties.FALLBACK_APPNAME);
285    }
286    }
287  0 potentialJavaBin.add(java);
288  0 potentialJavaBin.add(javaExe);
289  0 for (String name : potentialJavaBin)
290    {
291  0 if (name == null)
292    {
293  0 continue;
294    }
295  0 if (LaunchUtils.checkJVMSymlink(javaBinDir + name, winConsole))
296    {
297  0 javaBin = javaBinDir + name;
298  0 break;
299    }
300    }
301    }
302   
303  0 if (javaBin == null && generic)
304    {
305  0 javaBin = LaunchUtils.isWindows ? javaExe : java;
306    }
307   
308  0 return javaBin;
309    }
310   
311    /*
312    * checkJVMSymlink returns true if the path in testBin *is* a java binary, or
313    * points to a java binary.
314    * @param testBin The binary or symbolic link to check
315    * @param winConsole whether we are in/want a Windows console (only relevant for Windows,
316    * determines whether we use java.exe or javaw.exe)
317    */
 
318  0 toggle private static boolean checkJVMSymlink(String testBin, boolean winConsole)
319    {
320  0 File testBinFile = new File(testBin);
321  0 if (!testBinFile.exists())
322    {
323  0 return false;
324    }
325  0 File targetFile = null;
326  0 try
327    {
328  0 targetFile = testBinFile.getCanonicalFile();
329    } catch (IOException e)
330    {
331  0 return false;
332    }
333  0 final String javaExe = winConsole ? "java.exe" : "javaw.exe";
334  0 if (targetFile != null && ("java".equals(targetFile.getName())
335    || javaExe.equals(targetFile.getName())))
336    {
337  0 return true;
338    }
339  0 return false;
340    }
341   
342    /**
343    * Create a java command that matches the currently running java process and
344    * optionally remove/add some JVM and application parameters.
345    *
346    * @param String
347    * javaBinary The java binary to use. null uses the same as current
348    * process.
349    * @param String[]
350    * removeJvmArguments The (start of) JVM arguments to remove.
351    * @param String[]
352    * addJvmArguments JVM arguments to add.
353    * @param String[]
354    * prependToClasspath Add these dirs to the start of the classpath
355    * @param String[]
356    * appendToClasspath Add these dirs to the end of the classpath
357    * @param String[]
358    * deleteFromClasspath Remove these dirs from the existing classpath
359    * @param String
360    * startClass The name of the start class if different. null if the
361    * same.
362    * @param String[]
363    * removeAppArguments The (start of) application arguments to remove.
364    * @param String[]
365    * addAppArguments Application arguments to add.
366    * @param boolean
367    * terminate Flag to terminate this process after starting new
368    * process.
369    */
 
370  0 toggle public static int startNewJvm(String javaBinary,
371    List<String> removeJvmArguments, List<String> addJvmArguments,
372    List<String> prependToClasspath, List<String> appendToClasspath,
373    List<String> removeFromClasspath, String startClass,
374    List<String> removeAppArguments, List<String> addAppArguments,
375    List<String> appArguments, boolean launcherprint,
376    boolean launcherwait, boolean launcherstop, boolean debug,
377    boolean quiet)
378    {
379  0 if (javaBinary == null)
380    {
381  0 javaBinary = findJavaBin(false, true, true);
382    }
383   
384  0 List<String> classpathDirs = new ArrayList<>();
385  0 if (prependToClasspath != null)
386    {
387  0 classpathDirs.addAll(prependToClasspath);
388    }
389   
390  0 String classpath = ManagementFactory.getRuntimeMXBean().getClassPath();
391  0 if (removeFromClasspath != null)
392    {
393  0 Set<String> removeCp = new HashSet<>();
394  0 for (String dcp : removeFromClasspath)
395    {
396  0 try
397    {
398  0 String canPath = new File(dcp).getCanonicalPath();
399  0 removeCp.add(canPath);
400    } catch (IOException e)
401    {
402  0 ErrorLog.errPrintln(
403    "Problem getting canonical path. " + e.getMessage());
404    }
405    }
406  0 for (String cp : classpath.split(File.pathSeparator))
407    {
408  0 try
409    {
410  0 String canPath = new File(cp).getCanonicalPath();
411  0 if (!removeCp.contains(canPath))
412    {
413  0 classpathDirs.add(cp);
414    }
415    } catch (IOException e)
416    {
417  0 ErrorLog.errPrintln(
418    "Problem getting canonical path. " + e.getMessage());
419    }
420    }
421    }
422    else
423    {
424  0 classpathDirs
425    .addAll(Arrays.asList(classpath.split(File.pathSeparator)));
426    }
427  0 if (appendToClasspath != null)
428    {
429  0 classpathDirs.addAll(appendToClasspath);
430    }
431   
432  0 List<String> jvmArguments = new ArrayList<>();
433  0 List<String> originalJvmArguments = ManagementFactory.getRuntimeMXBean()
434    .getInputArguments();
435  0 if (removeJvmArguments != null)
436    {
437  0 for (String jvmArg : originalJvmArguments)
438    {
439  0 boolean addArg = true;
440  0 for (String rmArg : removeJvmArguments)
441    {
442  0 if (jvmArg.startsWith(rmArg))
443    {
444  0 addArg = false;
445  0 break;
446    }
447    }
448  0 if (addArg)
449    {
450  0 jvmArguments.add(jvmArg);
451    }
452    }
453    }
454    else
455    {
456  0 jvmArguments.addAll(originalJvmArguments);
457    }
458  0 if (addJvmArguments != null)
459    {
460  0 jvmArguments.addAll(addJvmArguments);
461    }
462   
463  0 if (startClass == null)
464    {
465    // this isn't always reliable
466  0 startClass = System.getProperty("sun.java.command");
467    }
468   
469  0 List<String> applicationArguments = new ArrayList<>();
470  0 if (removeAppArguments != null)
471    {
472  0 Set<String> removeArgs = new HashSet<>(removeAppArguments);
473  0 for (String appArg : appArguments)
474    {
475  0 if (!removeArgs.contains(removeArgs))
476    {
477  0 applicationArguments.add(appArg);
478    }
479    }
480    }
481    else
482    {
483  0 applicationArguments.addAll(appArguments);
484    }
485  0 if (addAppArguments != null)
486    {
487  0 applicationArguments.addAll(addAppArguments);
488    }
489   
490  0 List<String> command = new ArrayList<>();
491    // java command
492  0 command.add(javaBinary);
493   
494    // classpath
495  0 command.add("-cp");
496  0 command.add(String.join(File.pathSeparator, classpathDirs));
497   
498    // jvm args
499  0 command.addAll(jvmArguments);
500   
501    // start class
502  0 command.add(startClass);
503   
504    // application args
505  0 command.addAll(applicationArguments);
506   
507  0 return runProcess(command, launcherprint, launcherwait, launcherstop,
508    debug, quiet);
509    }
510   
 
511  0 toggle private static int runProcess(List<String> command, boolean launcherprint,
512    boolean launcherwait, boolean launcherstop, boolean debug,
513    boolean quiet)
514    {
515  0 final ProcessBuilder builder = new ProcessBuilder(command);
516  0 int exitValue = -1;
517   
518  0 if (Boolean.parseBoolean(System.getProperty("launcherprint", "false"))
519    || launcherprint)
520    {
521  0 syserr(debug, quiet,
522    "COMMAND: " + String.join(" ", builder.command()));
523    }
524   
525  0 if (Boolean.parseBoolean(System.getProperty("launcherstop", "false"))
526    || (debug && launcherstop))
527    {
528  0 syserr(debug, quiet,
529    "System property 'launcherstop' is set and not 'false'. Exiting.");
530  0 System.exit(0);
531    }
532  0 try
533    {
534  0 builder.inheritIO();
535  0 Process process = builder.start();
536  0 if (launcherwait)
537    {
538  0 syserr(debug, quiet, "Launching application process");
539  0 exitValue = process.waitFor();
540  0 syserr(debug, quiet,
541    "Application process return with value " + exitValue);
542    }
543    else
544    {
545  0 int waitInt = 0;
546  0 syserr(debug, quiet,
547    "Wait time for application process is " + waitInt + "ms");
548  0 if (process.waitFor(waitInt, TimeUnit.MILLISECONDS))
549    {
550  0 exitValue = process.exitValue();
551    }
552    else
553    {
554  0 exitValue = -2;
555    }
556    }
557  0 syserr(debug, quiet, "Launcher process ending");
558    } catch (IOException e)
559    {
560  0 if (e.getMessage().toLowerCase(Locale.ROOT).contains("memory"))
561    {
562  0 syserr(true, quiet, "Caught a memory exception: " + e.getMessage());
563    // Probably the "Cannot allocate memory" error, try without the memory
564    // setting
565  0 ArrayList<String> commandNoMem = new ArrayList<>();
566  0 for (int i = 0; i < command.size(); i++)
567    {
568  0 if (!command.get(i).startsWith("-Xmx"))
569    {
570  0 commandNoMem.add(command.get(i));
571    }
572    }
573  0 final ProcessBuilder builderNoMem = new ProcessBuilder(
574    commandNoMem);
575  0 syserr(true, quiet, "Command without memory setting: "
576    + String.join(" ", builderNoMem.command()));
577  0 try
578    {
579  0 builderNoMem.inheritIO();
580  0 Process processNoMem = builderNoMem.start();
581  0 exitValue = processNoMem.waitFor();
582    } catch (Exception ex)
583    {
584  0 ex.printStackTrace();
585    }
586    }
587    else
588    {
589  0 e.printStackTrace();
590    }
591    } catch (Exception e)
592    {
593  0 e.printStackTrace();
594    }
595  0 return exitValue;
596    }
597   
598    /**
599    * Look for Implementation-Version in two jar manifests and compare according
600    * to the getdown-launcher version spec (1.8.3-1.4.0_JVL or _FJVL) returns -1
601    * if f0 is newer (or more "valid"), +1 if f1 is newer (or more "valid"), 0 if
602    * the same or both equally "invalid"
603    *
604    * @param f0
605    * @param f1
606    * @return int
607    */
 
608  12 toggle public static int compareGetdownLauncherJarVersions(File f0, File f1)
609    {
610  12 if (!f0.exists() && !f1.exists())
611    {
612  0 return 0;
613    }
614  12 if (!f0.exists())
615    {
616  0 return -1;
617    }
618  12 if (!f1.exists())
619    {
620  0 return 1;
621    }
622  12 String v0 = getJarImplementationVersion(f0);
623  12 String v1 = getJarImplementationVersion(f1);
624  12 syserr(v0 != null && !v0.equals(v1), false,
625    "Got launcher versions '" + v0 + "' and '" + v1 + "'");
626  12 return compareGetdownLauncherJarVersions(v0, v1);
627    }
628   
 
629  26 toggle public static int compareGetdownLauncherJarVersions(String v0, String v1)
630    {
631  26 if (v0 == null && v1 == null)
632    {
633  0 return 0;
634    }
635  26 if (v0 == null)
636    {
637  0 return -1;
638    }
639  26 if (v1 == null)
640    {
641  0 return 1;
642    }
643   
644    // remove the subscript
645  26 if (v0.endsWith("JVL"))
646    {
647  14 v0 = v0.substring(0, v0.lastIndexOf('_'));
648    }
649  26 if (v1.endsWith("JVL"))
650    {
651  14 v1 = v1.substring(0, v1.lastIndexOf('_'));
652    }
653   
654  26 String[] v0parts = v0.split("-");
655  26 String[] v1parts = v1.split("-");
656   
657  26 int compare = 0;
658  49 for (int j = 0; j < Math.min(v0parts.length, v1parts.length); j++)
659    {
660  40 compare = compareVersions(v0parts[j], v1parts[j]);
661  40 if (compare != 0)
662    {
663  17 return compare;
664    }
665    }
666   
667  9 return v0parts.length - v1parts.length;
668    }
669   
670    /**
671    * comparing versions numbers of the form 1.2.3.4...n ONLY returns 0 if v0 and
672    * v1 are the same, a negative number if v0 < v1 and a positive number if v0 >
673    * v1. The number returned does NOT show how far apart the version numbers
674    * are.
675    */
 
676  49 toggle public static int compareVersions(String v0, String v1)
677    {
678  49 if (v0 == null && v1 == null)
679    {
680  0 return 0;
681    }
682  49 if (v0 == null)
683    {
684  0 return -1;
685    }
686  49 if (v1 == null)
687    {
688  0 return 1;
689    }
690  49 String[] v0dots = v0.split("\\.");
691  49 String[] v1dots = v1.split("\\.");
692  49 int compare = 0;
693  159 for (int i = 0; i < Math.min(v0dots.length, v1dots.length); i++)
694    {
695  129 if (!v0dots[i].equals(v1dots[i])) // avoids unnecessary
696    // NumberFormatException
697    {
698  19 try
699    {
700  19 compare = Integer.parseInt(v0dots[i])
701    - Integer.parseInt(v1dots[i]);
702    } catch (NumberFormatException e)
703    {
704  4 syserr(true, false, "Couldn't parse one of '" + v0dots[i]
705    + "' or '" + v1dots[i] + "': " + e.getMessage());
706  4 syserr(true, false, "Comparing as strings.");
707  4 compare = v0dots[i].compareTo(v1dots[i]);
708    }
709  19 if (compare != 0)
710    {
711  19 return compare;
712    }
713    }
714    }
715    // all numbers match up to min length. If one has more dots, assume it's
716    // a greater version (e.g. 1.3.2 > 1.3)
717  30 return v0dots.length - v1dots.length;
718    }
719   
 
720  28 toggle public static String getJarImplementationVersion(File jarFile)
721    {
722  28 String implementationVersion = null;
723  28 try
724    {
725  28 JarInputStream j0 = new JarInputStream(new FileInputStream(jarFile));
726  28 Manifest m0 = j0.getManifest();
727  28 if (m0 == null)
728    {
729  0 System.err.println("No manifest in " + jarFile.getAbsolutePath());
730    }
731    else
732    {
733  28 implementationVersion = m0.getMainAttributes()
734    .getValue(Attributes.Name.IMPLEMENTATION_VERSION);
735    }
736    } catch (IOException e)
737    {
738  0 System.err.println("Exception opening " + jarFile.getAbsolutePath()
739    + " to check version: " + e.getMessage());
740    }
741  28 return implementationVersion;
742    }
743   
 
744  20 toggle public static void syserr(boolean debug, boolean quiet, String message)
745    {
746  20 if (debug && !quiet)
747    {
748  16 ErrorLog.errPrintln("DEBUG - " + message);
749    }
750    }
751    }