package beast.app.beastapp; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.net.URLDecoder; import javax.swing.JOptionPane; import beast.app.util.Utils6; /** * Loads beast.jar and launches BEAST through the BEASTMain class * * This class should be compiled against 1.6 and packaged by itself. The * remainder of BEAST can be compiled against Java 1.8 * **/ public class BeastLauncher { private static String getVersion() {return "2.4.6";} private static String getMajorVersion() {return "2.4";} private static String pathDelimiter; public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException { if (javaVersionCheck("BEAST")) { loadBEASTJars(); Utils6.testCudaStatusOnMac(); BeastMain.main(args); } } /** * Load jars. The path is relative to the parent directory of the jar * containing this class, taking the lib directory. This is meant only to * load beast.jar and perhaps some other libraries, not all packages. **/ static protected void loadBEASTJars() throws IOException, NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { BeastLauncher clu = new BeastLauncher(); // first try beast from the package_user_dir/lib/beast.jar String beastUserDir = getPackageUserDir(); pathDelimiter = isWindows() ? "\\\\" : "/"; beastUserDir += pathDelimiter + "BEAST" + pathDelimiter; String beastJar = beastUserDir + "lib"; boolean foundJavaJarFile = checkForBEAST(new File(beastJar), clu); String launcherJar = clu.getClass().getProtectionDomain().getCodeSource().getLocation().getPath(); // deal with special characters and spaces in path launcherJar = URLDecoder.decode(launcherJar, "UTF-8"); System.err.println("jardir = " + launcherJar); File jarDir0 = new File(launcherJar).getParentFile(); while ((!foundJavaJarFile) && (jarDir0 != null)) { foundJavaJarFile = checkForBEAST(jarDir0, clu); foundJavaJarFile = foundJavaJarFile || checkForBEAST(new File(jarDir0.getAbsolutePath() + pathDelimiter +"lib"), clu); if (foundJavaJarFile) { createBeastPackage(jarDir0); } jarDir0 = jarDir0.getParentFile(); } if (!foundJavaJarFile) { System.err.println("WARNING: could not find beast.jar"); // if beast.jar or its classes are not already in the class path (as is when launched e.g. as developer) // the next line will fail } // initialise beast.jar Method method = Class.forName("beast.evolution.alignment.Alignment").getMethod("findDataTypes"); method.invoke(null); } private static void createBeastPackage(File jarDir0) { try { if (jarDir0.toString().toLowerCase().endsWith("lib")) { jarDir0 = jarDir0.getParentFile(); } // create package user dir, if it not already exists String userDir = getPackageUserDir(); File dir = new File(userDir + pathDelimiter + "BEAST" + pathDelimiter + "lib"); if (!dir.exists()) { if (!dir.mkdirs()) { // cannot create dir, let alone create a beast package return; } } File exampleDir = new File(userDir + pathDelimiter + "BEAST" + pathDelimiter + "examples" + pathDelimiter + "nexus"); if (!exampleDir.exists()) { if (!exampleDir.mkdirs()) { // cannot create dir, let alone create a beast package return; } } File templateDir = new File(userDir + pathDelimiter + "BEAST" + pathDelimiter + "templates"); if (!templateDir.exists()) { if (!templateDir.mkdirs()) { // cannot create dir, let alone create a beast package return; } } File beastJar = new File(jarDir0 + pathDelimiter + "lib" + pathDelimiter + "beast.jar"); File target = new File(dir + pathDelimiter + "beast.jar"); copyFileUsingStream(beastJar, target); String version = "<addon name='BEAST' version='" + getVersion() + "'>\n" + "</addon>"; FileWriter outfile = new FileWriter(userDir + pathDelimiter + "BEAST" + pathDelimiter + "version.xml"); outfile.write(version); outfile.close(); File beastSrcJar = new File(jarDir0 + pathDelimiter + "lib" + pathDelimiter + "beast.src.jar"); File srcTarget = new File(dir + pathDelimiter + "beast.src.jar"); copyFileUsingStream(beastSrcJar, srcTarget); copyFilesInDir(new File(jarDir0 + pathDelimiter + "examples"), new File(userDir + pathDelimiter + "BEAST" + pathDelimiter + "examples")); copyFilesInDir(new File(jarDir0 + pathDelimiter + "examples" + pathDelimiter + "nexus"), exampleDir); copyFilesInDir(new File(jarDir0 + pathDelimiter + "templates"), templateDir); // TODO: include templates? // if so, how to prevent clashes with templates in package and in installation dir? // TODO: what about examples? } catch (Exception e) { // do net let exceptions hold up launch of beast & friends e.printStackTrace(); } } private static void copyFilesInDir(File srcDir, File targetDir) throws IOException { String targetDirName = targetDir.getAbsolutePath(); for (File src : srcDir.listFiles()) { if (src.isFile()) { copyFileUsingStream(src, new File(targetDirName + pathDelimiter + src.getName())); } } } // copy files using Java 6 code private static void copyFileUsingStream(File source, File dest) throws IOException { InputStream is = null; OutputStream os = null; try { is = new FileInputStream(source); os = new FileOutputStream(dest); byte[] buffer = new byte[1024]; int length; while ((length = is.read(buffer)) > 0) { os.write(buffer, 0, length); } } finally { is.close(); os.close(); } } private static boolean checkForBEAST(File jarDir, Object clu) throws IOException { System.err.println("Checking out " + jarDir.getAbsolutePath()); boolean foundOne = false; if (jarDir.exists()) { URL url = new URL("file://" + (isWindows() ? "/" : "") + jarDir.getAbsolutePath() + "/beast.jar"); if (new File(jarDir.getAbsoluteFile()+File.separator+"beast.jar").exists()) { File versionFile = new File(jarDir.getParent() + pathDelimiter + "version.xml"); if (versionFile.exists()) { BufferedReader fin = new BufferedReader(new FileReader(versionFile)); String str = null; while (fin.ready()) { str += fin.readLine(); } fin.close(); int start = str.indexOf("version="); int end = str.indexOf("'", start + 9); String version = str.substring(start + 9, end); double localVersion = parseVersion(version); double desiredVersion = parseVersion(getVersion()); if (localVersion < desiredVersion) { return false; } } URLClassLoader sysLoader = (URLClassLoader) clu.getClass().getClassLoader(); Class<?> sysclass = URLClassLoader.class; try { // Parameters Class<?>[] parameters = new Class[] { URL.class }; Method method = sysclass.getDeclaredMethod("addURL", parameters); method.setAccessible(true); method.invoke(sysLoader, new Object[] { url }); System.err.println("Loaded URL " + url); foundOne = true; } catch (Throwable t) { t.printStackTrace(); throw new IOException("Error, could not add URL to system classloader"); } String classpath = System.getProperty("java.class.path"); String jar = url + ""; classpath += System.getProperty("path.separator") + jar.substring(5); System.setProperty("java.class.path", classpath); } } return foundOne; } static boolean isMac() { return System.getProperty("os.name").toLowerCase().startsWith("mac"); } static boolean isWindows() { return System.getProperty("os.name").toLowerCase().startsWith("windows"); } static boolean isLinux() { return System.getProperty("os.name").toLowerCase().startsWith("linux"); } /** make sure we run Java version 8 or better **/ static protected boolean javaVersionCheck(String app) { String javaVersion = System.getProperty("java.version"); // javaVersion should be something like "1.7.0_25" String[] version = javaVersion.split("\\."); if (version.length > 2) { try { int majorVersion = Integer.parseInt(version[1]); if (majorVersion <= 7) { String JAVA_VERSION_MSG = "<html>" + app + " requires Java version 8,<br>" + "but the current version is " + majorVersion + ".<br><br>" + "You can get Java from <a href='https://www.java.com/en/'>https://www.java.com/</a>.<br><br> " + "Continuing, but expect the unexpected.</html>"; if (!java.awt.GraphicsEnvironment.isHeadless()) { JOptionPane.showMessageDialog(null, JAVA_VERSION_MSG); } else { JAVA_VERSION_MSG = JAVA_VERSION_MSG.replaceAll("<br>", "\n"); JAVA_VERSION_MSG = JAVA_VERSION_MSG.replaceAll("<[^<]*>", ""); System.err.println(JAVA_VERSION_MSG); } return true; } } catch (NumberFormatException e) { // We only get here if the JVM does not return the expected // string format when asked for java.version. // hope for the best } return true; } // We only get here if the JVM does not return the expected // string format when asked for java.version. // hope for the best return true; } public static String getPackageUserDir() { if (System.getProperty("beast.user.package.dir") != null) return System.getProperty("beast.user.package.dir"); if (Utils6.isWindows()) { return System.getProperty("user.home") + "\\BEAST\\" + getMajorVersion(); } if (Utils6.isMac()) { return System.getProperty("user.home") + "/Library/Application Support/BEAST/" + getMajorVersion(); } // Linux and unices return System.getProperty("user.home") + "/.beast/" + getMajorVersion(); } /** Parse version string, assume it is of the form 1.2.3 * returns version where each sub-version is divided by 100, * so 2.0 -> return 2 * 2.1 return 2.01 * 2.2.3 return 2.0103 * Letters are ignored, so * 2.0.e -> 2.0 * 2.x.1 -> 2.0001 * @return */ private static double parseVersion(String versionString) { // is of the form 1.2.3 String [] strs = versionString.split("\\."); double version = 0; double divider = 1.0; for (int i = 0; i < strs.length; i++) { try { version += Double.parseDouble(strs[i]) / divider; divider = divider * 100.0; } catch (NumberFormatException e) { // ignore } } return version; } }