JAL-3820 JAL-4189 Move checks for java executable symlinks to LaunchUtils ready for...
[jalview.git] / src / jalview / util / LaunchUtils.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.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.net.MalformedURLException;
29 import java.net.URL;
30 import java.util.Properties;
31
32 public class LaunchUtils
33 {
34
35   // setting these is LaunchUtils so don't need to import Platform
36   public final static boolean isMac = System.getProperty("os.name")
37           .indexOf("Mac") > -1;
38
39   public final static boolean isWindows = System.getProperty("os.name")
40           .indexOf("Win") > -1;
41
42   private static boolean isJS = /** @j2sNative true || */
43           false;
44
45   public static void loadChannelProps(File dir)
46   {
47     ChannelProperties.loadProps(dir);
48   }
49
50   private static Properties userPreferences = null;
51
52   public static String getUserPreference(String key)
53   {
54     if (userPreferences == null)
55     {
56       String channelPrefsFilename = ChannelProperties
57               .getProperty("preferences.filename");
58       if (channelPrefsFilename == null)
59       {
60         return null;
61       }
62       File propertiesFile = new File(System.getProperty("user.home"),
63               channelPrefsFilename);
64       if (!propertiesFile.exists())
65       {
66         return null;
67       }
68       try
69       {
70         userPreferences = new Properties();
71         userPreferences.load(new FileInputStream(propertiesFile));
72       } catch (FileNotFoundException e)
73       {
74         // didn't find user preferences file
75         return null;
76       } catch (IOException e)
77       {
78         System.err.println(e.getMessage());
79         return null;
80       }
81     }
82     return userPreferences.getProperty(key);
83   }
84
85   public static boolean getBooleanUserPreference(String key)
86   {
87     return Boolean.parseBoolean(getUserPreference(key));
88   }
89
90   public static int JAVA_COMPILE_VERSION = 0;
91
92   public static int getJavaCompileVersion()
93   {
94     if (LaunchUtils.isJS)
95     {
96       return -1;
97     }
98     else if (JAVA_COMPILE_VERSION > 0)
99     {
100       return JAVA_COMPILE_VERSION;
101     }
102     String buildDetails = "jar:".concat(LaunchUtils.class
103             .getProtectionDomain().getCodeSource().getLocation().toString()
104             .concat("!" + "/.build_properties"));
105     try
106     {
107       URL localFileURL = new URL(buildDetails);
108       InputStream in = localFileURL.openStream();
109       Properties buildProperties = new Properties();
110       buildProperties.load(in);
111       in.close();
112       String JCV = buildProperties.getProperty("JAVA_COMPILE_VERSION",
113               null);
114       if (JCV == null)
115       {
116         System.out.println(
117                 "Could not obtain JAVA_COMPILE_VERSION for comparison");
118         return -2;
119       }
120       JAVA_COMPILE_VERSION = Integer.parseInt(JCV);
121     } catch (MalformedURLException e)
122     {
123       System.err.println("Could not find " + buildDetails);
124       return -3;
125     } catch (IOException e)
126     {
127       System.err.println("Could not load " + buildDetails);
128       return -4;
129     } catch (NumberFormatException e)
130     {
131       System.err.println("Could not parse JAVA_COMPILE_VERSION");
132       return -5;
133     }
134
135     return JAVA_COMPILE_VERSION;
136   }
137
138   public static int JAVA_VERSION = 0;
139
140   public static int getJavaVersion()
141   {
142     if (LaunchUtils.isJS)
143     {
144       return -1;
145     }
146     else if (JAVA_VERSION > 0)
147     {
148       return JAVA_VERSION;
149     }
150     try
151     {
152       String JV = System.getProperty("java.version");
153       if (JV == null)
154       {
155         System.out.println("Could not obtain java.version for comparison");
156         return -2;
157       }
158       if (JV.startsWith("1."))
159       {
160         JV = JV.substring(2);
161       }
162       JAVA_VERSION = JV.indexOf(".") == -1 ? Integer.parseInt(JV)
163               : Integer.parseInt(JV.substring(0, JV.indexOf(".")));
164     } catch (NumberFormatException e)
165     {
166       System.err.println("Could not parse java.version");
167       return -3;
168     }
169     return JAVA_VERSION;
170   }
171
172   public static boolean checkJavaVersion()
173   {
174     if (LaunchUtils.isJS)
175     {
176       return true;
177     }
178     String buildDetails = "jar:".concat(LaunchUtils.class
179             .getProtectionDomain().getCodeSource().getLocation().toString()
180             .concat("!" + "/.build_properties"));
181
182     int java_compile_version = getJavaCompileVersion();
183     int java_version = getJavaVersion();
184
185     if (java_compile_version <= 0 || java_version <= 0)
186     {
187       System.out.println("Could not make Java version check");
188       return true;
189     }
190     // Warn if these java.version and JAVA_COMPILE_VERSION conditions exist
191     // Usually this means a Java 11 compiled JAR being run by a Java 11 JVM
192     if (java_version >= 11 && java_compile_version < 11)
193     {
194       return false;
195     }
196
197     return true;
198   }
199
200   public static String findJavaBin(boolean winConsole)
201   {
202     return findJavaBin(System.getProperty("java.home"), winConsole, true);
203   }
204
205   /*
206    * Returns a string path to the most likely java binary wanted to run this
207    * installation of Jalview.
208    * 
209    * @param  winConsole  whether to use java.exe (console) in preference to javaw.exe
210    *                     (only affects Windows).
211    * @param  javaHome    Try this javaHome dir (defaults to the running java.home).
212    * @param  generic     Return a generic java command if not found.
213    */
214   public static String findJavaBin(String javaHome, boolean winConsole,
215           boolean generic)
216   {
217     String javaBin = null;
218     final String javaExe = winConsole ? "java.exe" : "javaw.exe";
219     final String java = "java";
220
221     if (javaHome != null)
222     {
223       // property "channel.app_name" is set by install4j when launching getdown
224       String propertyAppName = System.getProperty("channel.app_name");
225       final String appName = (propertyAppName != null
226               && propertyAppName.length() > 0) ? propertyAppName
227                       : ChannelProperties.getProperty("app_name");
228
229       final String javaBinDir = javaHome + File.separator + "bin"
230               + File.separator;
231
232       // appName and "Jalview" will not point to javaw.exe or java.exe but in
233       // this case that's okay because the taskbar display name problem doesn't
234       // manifest in Windows. See JAL-3820, JAL-4189.
235       for (String name : new String[] { appName, "Jalview", java, javaExe })
236       {
237         if (LaunchUtils.checkJVMSymlink(javaBinDir + name, winConsole))
238         {
239           javaBin = javaBinDir + name;
240           break;
241         }
242       }
243     }
244
245     if (javaBin == null && generic)
246     {
247       javaBin = LaunchUtils.isWindows ? javaExe : java;
248     }
249
250     return javaBin;
251   }
252
253   /*
254    * checkJVMSymlink returns true if the path in testBin *is* a java binary, or
255    * points to a java binary.
256    * @param  testBin     The binary or symbolic link to check
257    * @param  winConsole  whether we are in/want a Windows console (only relevant for Windows,
258    *                     determines whether we use java.exe or javaw.exe)
259    */
260   private static boolean checkJVMSymlink(String testBin, boolean winConsole)
261   {
262     File testBinFile = new File(testBin);
263     if (!testBinFile.exists())
264     {
265       return false;
266     }
267     File targetFile = null;
268     try
269     {
270       targetFile = testBinFile.getCanonicalFile();
271     } catch (IOException e)
272     {
273       return false;
274     }
275     final String javaExe = winConsole ? "java.exe" : "javaw.exe";
276     if (targetFile != null && ("java".equals(targetFile.getName())
277             || javaExe.equals(targetFile.getName())))
278     {
279       return true;
280     }
281     return false;
282   }
283 }