/*
* Capsule
* Copyright (c) 2014-2015, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are licensed under the terms
* of the Eclipse Public License v1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package co.paralleluniverse.capsule;
import com.google.common.jimfs.Jimfs;
import static com.google.common.truth.Truth.assert_;
import java.io.IOException;
import java.io.InputStream;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author pron
*/
public class CapsuleLauncherTest {
private final FileSystem fs = Jimfs.newFileSystem();
private final Path cache = fs.getPath("/cache");
private final Path tmp = fs.getPath("/tmp");
@After
public void tearDown() throws Exception {
fs.close();
}
@Test
public void testSimpleExtract() throws Exception {
Jar jar = newCapsuleJar()
.setAttribute("Application-Class", "com.acme.Foo")
.setAttribute("Unregisterd-Attribute", "just a string")
.addEntry("foo.jar", emptyInputStream())
.addEntry("a.class", emptyInputStream())
.addEntry("b.txt", emptyInputStream())
.addEntry("lib/a.jar", emptyInputStream())
.addEntry("lib/b.class", emptyInputStream())
.addEntry("q/w/x.txt", emptyInputStream())
.addEntry("d\\f\\y.txt", emptyInputStream()) // test with Windows path
.addEntry("META-INF/x.txt", emptyInputStream());
List<String> args = list("hi", "there");
List<String> cmdLine = list();
Properties props = new Properties(System.getProperties());
props.setProperty("my.foo.prop", "zzzz");
Capsule capsule = newCapsuleLauncher(jar).setProperties(props).newCapsule();
ProcessBuilder pb = capsule.prepareForLaunch(cmdLine, args);
assertTrue(capsule.hasAttribute(Attribute.named("Application-Class")));
assertEquals("com.acme.Foo", capsule.getAttribute(Attribute.named("Application-Class")));
assertTrue(capsule.hasAttribute(Attribute.named("Unregisterd-Attribute")));
assertEquals("just a string", capsule.getAttribute(Attribute.named("Unregisterd-Attribute")));
// dumpFileSystem(fs);
assertEquals(capsule.getProperties().getProperty("my.foo.prop"), "zzzz");
assertEquals(args, getAppArgs(pb));
Path appCache = cache.resolve("apps").resolve("com.acme.Foo");
assertEquals("com.acme.Foo", getProperty(pb, "capsule.app"));
assertEquals("com.acme.Foo", getEnv(pb, "CAPSULE_APP"));
assertEquals(appCache, path(getProperty(pb, "capsule.dir")));
assertEquals(absolutePath("capsule.jar"), path(getProperty(pb, "capsule.jar")));
assertEquals(appCache, path(getEnv(pb, "CAPSULE_DIR")));
assertEquals(absolutePath("capsule.jar"), path(getEnv(pb, "CAPSULE_JAR")));
assertEquals(list("com.acme.Foo", "hi", "there"), getMainAndArgs(pb));
assertTrue(Files.isDirectory(cache));
assertTrue(Files.isDirectory(cache.resolve("apps")));
assertTrue(Files.isDirectory(appCache));
assertTrue(Files.isRegularFile(appCache.resolve(".extracted")));
assertTrue(Files.isRegularFile(appCache.resolve("foo.jar")));
assertTrue(Files.isRegularFile(appCache.resolve("b.txt")));
assertTrue(Files.isDirectory(appCache.resolve("lib")));
assertTrue(Files.isRegularFile(appCache.resolve("lib").resolve("a.jar")));
assertTrue(!Files.isRegularFile(appCache.resolve("a.class")));
assertTrue(!Files.isRegularFile(appCache.resolve("lib").resolve("b.class")));
assertTrue(!Files.isDirectory(appCache.resolve("META-INF")));
assertTrue(!Files.isRegularFile(appCache.resolve("META-INF").resolve("x.txt")));
assertTrue(Files.isDirectory(appCache.resolve("q").resolve("w")));
assertTrue(Files.isDirectory(appCache.resolve("d").resolve("f")));
assertTrue(Files.isRegularFile(appCache.resolve("q").resolve("w").resolve("x.txt")));
assertTrue(Files.isRegularFile(appCache.resolve("d").resolve("f").resolve("y.txt")));
// assert_().that(getClassPath(pb)).has().item(absolutePath("capsule.jar"));
assert_().that(getClassPath(pb)).has().item(appCache.resolve("foo.jar"));
assert_().that(getClassPath(pb)).has().noneOf(appCache.resolve("lib").resolve("a.jar"));
}
@Test
public void testEnableJMX() throws Exception {
assert_().that(CapsuleLauncher.enableJMX(list("a", "b"))).has().item("-Dcom.sun.management.jmxremote");
assert_().that(CapsuleLauncher.enableJMX(list("a", "-Dcom.sun.management.jmxremote", "b"))).isEqualTo(list("a", "-Dcom.sun.management.jmxremote", "b"));
}
//<editor-fold defaultstate="collapsed" desc="Utilities">
/////////// Utilities ///////////////////////////////////
private Jar newCapsuleJar() {
return new Jar()
.setAttribute("Manifest-Version", "1.0")
.setAttribute("Main-Class", "Capsule")
.setAttribute("Premain-Class", "Capsule");
}
private CapsuleLauncher newCapsuleLauncher(Jar jar) throws IOException {
Path capsuleJar = path("capsule.jar");
jar.write(capsuleJar);
return new CapsuleLauncher(capsuleJar).setCacheDir(cache);
}
private Path path(String first, String... more) {
return fs.getPath(first, more);
}
private Path absolutePath(String first, String... more) {
return fs.getPath(first, more).toAbsolutePath();
}
private InputStream emptyInputStream() {
return Jar.toInputStream("", UTF_8);
}
private List<Path> paths(String cp) {
final List<Path> res = new ArrayList<>();
for (String p : cp.split(":"))
res.add(path(p));
return res;
}
private List<Path> getClassPath(ProcessBuilder pb) {
final List<String> cmd = pb.command();
final int i = cmd.indexOf("-classpath");
if (i < 0)
return null;
final String cp = cmd.get(i + 1);
// return Arrays.asList(cp.split(":"));
return paths(cp);
}
private String getProperty(ProcessBuilder pb, String prop) {
return getOption(pb, "-D" + prop, '=');
}
private String getEnv(ProcessBuilder pb, String envVar) {
return pb.environment().get(envVar);
}
private String getOption(ProcessBuilder pb, String opt) {
return getOption(pb, opt, ':');
}
private String getOption(ProcessBuilder pb, String opt, char separator) {
final List<String> jvmargs = getJvmArgs(pb);
for (String a : jvmargs) {
if (a.startsWith(opt)) {
String res = getAfter(a, separator);
return res != null ? res : "";
}
}
return null;
}
private List<String> getJvmArgs(ProcessBuilder pb) {
boolean classpath = false;
int i = 0;
for (String x : pb.command().subList(1, pb.command().size())) {
if (x.equals("-jar") || (!x.startsWith("-") && !classpath))
break;
if (x.equals("-classpath") || x.equals("-cp"))
classpath = true;
else
classpath = false;
i++;
}
return pb.command().subList(1, i + 1);
}
private String getMainJar(ProcessBuilder pb) {
final List<String> cmd = pb.command();
final int start = getJvmArgs(pb).size() + 1;
if (cmd.get(start).equals("-jar"))
return cmd.get(start + 1);
return null;
}
private String getMainClass(ProcessBuilder pb) {
final List<String> cmd = pb.command();
final int start = getJvmArgs(pb).size() + 1;
if (cmd.get(start).equals("-jar"))
return null;
return cmd.get(start);
}
private List<String> getAppArgs(ProcessBuilder pb) {
List<String> jvmArgs = getJvmArgs(pb);
final List<String> cmd = pb.command();
final int start = jvmArgs.size() + 1;
return cmd.subList(start + (cmd.get(start).equals("-jar") ? 2 : 1), cmd.size());
}
private static List<String> getMainAndArgs(ProcessBuilder pb) {
List<String> cmd = pb.command();
cmd = cmd.subList(1, cmd.size());
boolean prevClassPath = false;
int i = 0;
for (String c : cmd) {
if (c.startsWith("-") || prevClassPath)
i++;
else
break;
prevClassPath = c.equals("-classpath");
}
return cmd.subList(i, cmd.size());
}
private List<Path> toPath(List<String> ps) {
final List<Path> pss = new ArrayList<Path>(ps.size());
for (String p : ps)
pss.add(path(p));
return pss;
}
private List<Path> toAbsolutePath(List<Path> ps) {
final List<Path> pss = new ArrayList<Path>(ps.size());
for (Path p : ps)
pss.add(p.toAbsolutePath().normalize());
return pss;
}
private static String getBefore(String s, char separator) {
final int i = s.indexOf(separator);
if (i < 0)
return s;
return s.substring(0, i);
}
private static String getAfter(String s, char separator) {
final int i = s.indexOf(separator);
if (i < 0)
return null;
return s.substring(i + 1);
}
@SafeVarargs
private static <T> List<T> list(T... xs) {
return Arrays.asList(xs);
}
//</editor-fold>
}