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;
}
}