/******************************************************************************* * Copyright (c) 2008 Vlad Dumitrescu and others. All rights reserved. This program and * the accompanying materials are made available under the terms of the Eclipse Public * License v1.0 which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: Vlad Dumitrescu *******************************************************************************/ package org.erlide.runtime.runtimeinfo; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import org.eclipse.jdt.annotation.NonNull; import com.google.common.base.Charsets; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.io.Files; public final class RuntimeInfo { private final String name; private final String otpHomeDir; private final String args; private final Collection<String> codePath; private final boolean valid; private RuntimeVersion version = null; @NonNull public static final RuntimeInfo NO_RUNTIME_INFO = new RuntimeInfo(""); public static class Builder { private String name; private String homeDir; private String args; private Collection<String> codePath; public Builder() { name = ""; homeDir = "."; args = ""; codePath = Lists.newArrayList(); } public Builder(@NonNull final RuntimeInfo info) { name = info.getName(); homeDir = info.getOtpHome(); args = info.getArgs(); codePath = info.getCodePath(); } @NonNull public RuntimeInfo build() { return new RuntimeInfo(name, homeDir, args, codePath); } public Builder withName(final String aName) { name = aName; return this; } public Builder withHomeDir(final String aHomeDir) { homeDir = aHomeDir; return this; } public Builder withArgs(final String someArgs) { args = someArgs; return this; } public Builder withCodePath(final Collection<String> aCodePath) { codePath = aCodePath; return this; } } public RuntimeInfo(final String name) { this(name, ".", "", new ArrayList<String>()); } public RuntimeInfo(final String name, final String otpHomeDir, final String args, final Collection<String> codePath) { Preconditions.checkArgument(name != null); Preconditions.checkArgument(otpHomeDir != null); Preconditions.checkArgument(args != null); Preconditions.checkArgument(codePath != null); this.name = name; this.otpHomeDir = otpHomeDir; this.args = args; this.codePath = ImmutableList.copyOf(codePath); valid = isValidOtpHome(otpHomeDir); } public RuntimeInfo(@NonNull final RuntimeInfo o) { this(o.name, o.otpHomeDir, o.args, o.codePath); } public String getArgs() { return args; } @Override public String toString() { return String.format("Runtime<%s (%s) %s [%s]>", getName(), getOtpHome(), getVersion(), getArgs()); } public String getOtpHome() { return otpHomeDir; } public String getName() { return name; } public Collection<String> getCodePath() { return codePath; } @Override public boolean equals(final Object other) { if (!(other instanceof RuntimeInfo)) { return false; } final RuntimeInfo other1 = (RuntimeInfo) other; return Objects.equal(otpHomeDir + "|" + args + "|" + codePath.toString(), other1.otpHomeDir + "|" + other1.args + "|" + other1.codePath.toString()); } @Override public int hashCode() { return Objects.hashCode(otpHomeDir, args, codePath); } public boolean isValid() { return valid; } public static boolean validateLocation(final String path) { final String v = getRuntimeVersion(path); return v != null; } public static boolean isValidOtpHome(final String otpHome) { // Check if it looks like a ERL_TOP location: if (Strings.isNullOrEmpty(otpHome)) { return false; } final File d = new File(otpHome); if (!d.isDirectory()) { return false; } final boolean hasErl = hasExecutableFile(otpHome + "/bin/erl"); final File lib = new File(otpHome + "/lib"); final boolean hasLib = lib.isDirectory() && lib.exists(); return hasErl && hasLib; } private static boolean hasExecutableFile(final String fileName) { final File simpleFile = new File(fileName); final File exeFile = new File(fileName + ".exe"); return simpleFile.exists() || exeFile.exists(); } public static boolean hasCompiler(final String otpHome) { if (!isValidOtpHome(otpHome)) { return false; } final boolean hasErlc = hasExecutableFile(otpHome + "/bin/erlc"); return hasErlc; } protected static String cvt(final Collection<String> path) { final StringBuilder result = new StringBuilder(); for (String s : path) { if (s.length() > 0) { if (s.contains(" ")) { s = "\"" + s + "\""; } result.append(s).append(';'); } } return result.toString(); } public RuntimeVersion getVersion() { if (version == null) { version = getVersion(otpHomeDir); } return version; } public static RuntimeVersion getVersion(final String homeDir) { final String label = getRuntimeVersion(homeDir); final String micro = getMicroRuntimeVersion(homeDir); return RuntimeVersion.Serializer.parse(label, micro); } public static String getRuntimeVersion(final String path) { // System.out.println("> get version for " + path); if (path == null) { return null; } String result = readOtpVersion(path); // System.out.println("> root: " + result); if (result != null) { return result; } result = readReleaseOtpVersion(path + "/releases/"); // System.out.println("> releases: " + result); if (result != null) { return result; } result = readStartBoot(path); // System.out.println("> boot: " + result); return result; } private static String readOtpVersion(final String path) { final File file = new File(path + "/OTP_VERSION"); try { return Files.toString(file, Charsets.US_ASCII).trim(); } catch (final IOException e) { } return null; } private static String readReleaseOtpVersion(final String path) { final File dir = new File(path); final String[] rels = dir.list(); if (rels == null) { return null; } // sort! for (final String rel : rels) { final String result = readOtpVersion(path + rel); // System.out.println("> check: '" + path + rel + "'"); if (result != null) { return result; } } return null; } private static String readStartBoot(final String path) { String result = null; final File file = new File(path + "/bin/start.boot"); try (final FileInputStream is = new FileInputStream(file)) { is.skip(14); readstring(is); result = readstring(is); } catch (final IOException e) { } return result; } public static String getMicroRuntimeVersion(final String path) { if (path == null) { return null; } String result = null; // now get micro version from kernel's minor version final File lib = new File(path + "/lib"); final File[] kernels = lib.listFiles(new FileFilter() { @Override public boolean accept(final File pathname) { try { boolean r = pathname.isDirectory(); r &= pathname.getName().startsWith("kernel-"); final String canonicalPath = pathname.getCanonicalPath() .toLowerCase(); final String absolutePath = pathname.getAbsolutePath().toLowerCase(); r &= canonicalPath.equals(absolutePath); return r; } catch (final IOException e) { return false; } } }); if (kernels != null && kernels.length > 0) { final int[] krnls = new int[kernels.length]; for (int i = 0; i < kernels.length; i++) { final String k = kernels[i].getName(); try { int p = k.indexOf('.'); if (p < 0) { krnls[i] = 0; } else { p = k.indexOf('.', p + 1); if (p < 0) { krnls[i] = 0; } else { krnls[i] = Integer.parseInt(k.substring(p + 1)); } } } catch (final Exception e) { krnls[i] = 0; } } Arrays.sort(krnls); result = Integer.toString(krnls[krnls.length - 1]); } return result; } static String readstring(final InputStream is) { try { is.read(); byte[] b = new byte[2]; is.read(b); final int len = b[0] * 256 + b[1]; b = new byte[len]; is.read(b); final String s = new String(b); return s; } catch (final IOException e) { return null; } } }