package io.cattle.platform.packaging; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.security.CodeSource; import java.security.ProtectionDomain; import java.util.Arrays; import java.util.UUID; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.jar.Pack200; import java.util.jar.Pack200.Unpacker; import java.util.zip.ZipEntry; public class Bootstrap implements Closeable { public static final String RESOURCES = "resources.jar"; public static final String MAIN = "io.cattle.platform.launcher.Main"; boolean war = false; URL[] classpath = null; File home = null; File libDir = null; File tempDir = null; JarOutputStream warOutput = null; String warName = null; boolean inEntry = false; String version = null; protected void setHomeAndEnv() throws IOException { String home = System.getenv("CATTLE_HOME"); if (home == null) { home = System.getProperty("cattle.home"); } if (home == null) { home = new File(System.getProperty("user.home"), ".cattle") + File.separator; } if (!home.endsWith(File.separator)) { home = home + File.separator; } System.setProperty("cattle.home", home); File homeFile = new File(home); if (!homeFile.exists() && !homeFile.mkdirs()) { throw new IOException("Failed to create [" + homeFile.getAbsolutePath() + "]"); } this.home = new File(home); if (System.getProperty("logback.bootstrap.level") == null) { System.setProperty("logback.bootstrap.level", "WARN"); } String lib = System.getenv("CATTLE_LIB"); if (lib == null) { lib = System.getProperty("cattle.lib"); } if (lib != null) { System.setProperty("cattle.lib", lib); } if (lib != null) { libDir = new File(lib); } } protected void mkdirs(File dir) throws IOException { if (!dir.exists() && !dir.mkdirs()) { throw new IOException("Failed to create directory [" + dir + "]"); } } protected JarInputStream getInput() throws IOException { String input = System.getenv("INPUT"); if (input != null) { return new JarInputStream(new FileInputStream(input)); } ProtectionDomain domain = Bootstrap.class.getProtectionDomain(); CodeSource source = domain.getCodeSource(); return new JarInputStream(source.getLocation().openStream()); } public static void closeQuietly(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException ioe) { } } protected String getCattleId() throws IOException { JarInputStream is = getInput(); try { JarEntry entry = null; while ((entry = is.getNextJarEntry()) != null) { if (!entry.getName().equals(RESOURCES)) { continue; } JarInputStream ris = new JarInputStream(is); try { return ris.getManifest().getMainAttributes().getValue("X-cattle-id"); } finally { closeQuietly(ris); } } } finally { closeQuietly(is); } return null; } protected void determineLibDir(JarInputStream is) throws IOException { version = getVersion(is); if (version == null) { System.err.println("No cattle version found in jar"); version = UUID.randomUUID().toString(); } if (libDir == null) { libDir = new File(new File(home, "lib"), version); } } protected void extractLib(JarInputStream is) throws IOException { if (libDir.exists()) { return; } if (war) { System.out.println("[BOOTSTRAP] Creating " + warName); } else { System.out.println("[BOOTSTRAP] Running first time extraction"); } extractFiles(is, true); System.out.println("\n[BOOTSTRAP] Done"); if (tempDir != null) { tempDir.renameTo(libDir); tempDir = null; } } protected int count(JarInputStream is) throws IOException { int count = 0; JarEntry entry = null; while ((entry = is.getNextJarEntry()) != null) { if (!entry.isDirectory()) count++; } return count; } protected void extractFiles(JarInputStream is, boolean first) throws IOException { Unpacker unpacker = Pack200.newUnpacker(); int total = first ? count(getInput()) : 0; int done = 0; int lastPrinted = -1; int every = 10; JarEntry entry = null; while ((entry = is.getNextJarEntry()) != null) { String name = entry.getName(); if (name.equals(RESOURCES)) { extractFiles(new JarInputStream(new NoCloseInputStream(is)), false); } else if (name.endsWith(".pack")) { name = name.substring(0, name.length() - 5); JarOutputStream os = new JarOutputStream(getOutputStream(name, false)); os.setLevel(0); try { unpacker.unpack(new NoCloseInputStream(is), os); } finally { closeQuietly(os); } done++; } else if (!entry.isDirectory()) { OutputStream os = getOutputStream(name, first ? !name.endsWith(".jar") : first); if (os == null) { continue; } try { byte[] buffer = new byte[8192]; int count = -1; while ((count = is.read(buffer)) > 0) { os.write(buffer, 0, count); } } finally { closeQuietly(os); } done++; } if (first) { int progress = ((done * 100) / total) / every; if (lastPrinted != progress) { System.out.print((progress * every) + "% "); lastPrinted = progress; } } } } protected OutputStream getOutputStream(String name, boolean rootResource) throws IOException { if (warOutput == null) { File root = rootResource ? home : tempDir; File outputFile = new File(root, name); if (rootResource) { if (!name.startsWith("etc") && !name.startsWith("extensions")) { return null; } if (outputFile.exists()) { return null; } } mkdirs(outputFile.getParentFile()); return new FileOutputStream(outputFile); } else { if (rootResource) { return null; } if (inEntry) { warOutput.closeEntry(); } warOutput.putNextEntry(new ZipEntry(name)); inEntry = true; return new NoCloseOutputStream(warOutput); } } protected void createOutput() throws IOException { if (war) { Manifest manifest = null; JarInputStream jis = getInput(); JarEntry entry = null; while ((entry = jis.getNextJarEntry()) != null) { if (RESOURCES.equals(entry.getName())) { JarInputStream resourceIs = new JarInputStream(jis); try { manifest = resourceIs.getManifest(); break; } finally { closeQuietly(resourceIs); } } } try { warName = "cattle-" + version + ".war"; warOutput = new JarOutputStream(new FileOutputStream(warName), manifest); } finally { closeQuietly(jis); } } else { tempDir = new File(libDir.getAbsolutePath() + "-" + UUID.randomUUID().toString()); if (!tempDir.mkdirs()) { throw new IOException("Failed to create [" + tempDir.getAbsolutePath() + "]"); } } } protected void determineClasspath() throws IOException { classpath = new URL[] { libDir.toURI().toURL() }; } protected String getVersion(JarInputStream input) throws IOException { boolean close = false; try { if (input == null) { close = true; input = getInput(); } Manifest m = input.getManifest(); String impl = m.getMainAttributes().getValue("Implementation-Version"); String scm = m.getMainAttributes().getValue("SCM-Revision"); if (impl == null) { return "dev"; } if (impl.contains("SNAPSHOT") && scm != null) { return impl + "-" + scm + "-" + getCattleId(); } return impl; } finally { if (close) { closeQuietly(input); } } } public void run(String... args) throws Exception { for (String arg : args) { if (arg.contains("version")) { System.out.println(getVersion(null)); System.exit(0); } else if (arg.equals("war")) { war = true; } } System.out.println("[BOOTSTRAP] Starting Cattle"); if (war) { if (args.length == 1) { args = new String[0]; } else { args = Arrays.copyOfRange(args, 1, args.length - 1); } } JarInputStream is = null; Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { closeQuietly(Bootstrap.this); } }); try { setHomeAndEnv(); is = getInput(); determineLibDir(is); System.out.println("[BOOTSTRAP] CATTLE_HOME=" + this.home.getAbsolutePath()); System.out.println("[BOOTSTRAP] CATTLE_LIB=" + this.libDir.getAbsolutePath()); createOutput(); extractLib(is); closeQuietly(is); closeQuietly(this); if (war) { return; } determineClasspath(); System.out.println("[BOOTSTRAP] Launching Cattle from " + Arrays.toString(classpath)); try (URLClassLoader cl = new URLClassLoader(classpath, Bootstrap.class.getClassLoader())) { Class<?> mainClass = cl.loadClass(MAIN); Method m = mainClass.getMethod("main", String[].class); m.invoke(mainClass, new Object[] { args }); } } finally { closeQuietly(is); } } @Override public void close() throws IOException { if (warOutput != null) { closeQuietly(warOutput); warOutput = null; } if (tempDir != null && tempDir.exists()) { delete(tempDir); tempDir = null; } } protected void delete(File dir) { if (dir == null) { return; } File[] children = dir.listFiles(); if (children != null) { for (File child : children) { if (child.isDirectory()) { delete(child); } else { child.delete(); } } } dir.delete(); } public static final void main(String... args) { try (Bootstrap b = new Bootstrap()) { b.run(args); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } }