/*
* Capsule
* Copyright (c) 2014-2015, Parallel Universe Software Co. and Contributors. 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 co.paralleluniverse.common.JarClassLoader;
import co.paralleluniverse.common.ZipFS;
import com.google.common.jimfs.Jimfs;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
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.nio.file.Paths;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*
* @author pron
*/
public class JarTest {
@Test
public void testCreateJar() throws Exception {
ByteArrayOutputStream res = new Jar()
.setAttribute("Foo", "1234")
.setAttribute("Bar", "5678")
.addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.write(new ByteArrayOutputStream());
// printEntries(toInput(res));
assertEquals("I am foo!\n", getEntryAsString(toInput(res), Paths.get("foo.txt"), UTF_8));
assertEquals("I am bar!\n", getEntryAsString(toInput(res), Paths.get("dir", "bar.txt"), UTF_8));
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
assertEquals("5678", man2.getMainAttributes().getValue("Bar"));
}
@Test
public void testUpdateJar() throws Exception {
FileSystem fs = Jimfs.newFileSystem();
Path jarPath = fs.getPath("test.jar");
try {
// create
new Jar()
.setAttribute("Foo", "1234")
.setAttribute("Bar", "5678")
.setListAttribute("List", Arrays.asList("a", "b"))
.setMapAttribute("Map", new HashMap<String, String>() {
{
put("x", "1");
put("y", "2");
}
})
.addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.write(jarPath);
// update
Jar jar = new Jar(jarPath);
ByteArrayOutputStream res = jar
.setAttribute("Baz", "hi!")
.setAttribute("Bar", "8765")
.setListAttribute("List", addLast(addFirst(jar.getListAttribute("List"), "0"), "c"))
.setMapAttribute("Map", put(put(jar.getMapAttribute("Map", null), "z", "3"), "x", "0"))
.addEntry(Paths.get("dir", "baz.txt"), Jar.toInputStream("And I am baz!\n", UTF_8))
.write(new ByteArrayOutputStream());
// test
// printEntries(toInput(res));
assertEquals("I am foo!\n", getEntryAsString(toInput(res), Paths.get("foo.txt"), UTF_8));
assertEquals("I am bar!\n", getEntryAsString(toInput(res), Paths.get("dir", "bar.txt"), UTF_8));
assertEquals("And I am baz!\n", getEntryAsString(toInput(res), Paths.get("dir", "baz.txt"), UTF_8));
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
assertEquals("8765", man2.getMainAttributes().getValue("Bar"));
assertEquals("hi!", man2.getMainAttributes().getValue("Baz"));
assertEquals(Arrays.asList("0", "a", "b", "c"), new Jar(toInput(res)).getListAttribute("List"));
assertEquals(new HashMap<String, String>() {
{
put("x", "0");
put("y", "2");
put("z", "3");
}
}, new Jar(toInput(res)).getMapAttribute("Map", null));
} finally {
Files.delete(jarPath);
}
}
@Test
public void testUpdateJar2() throws Exception {
// create
ByteArrayOutputStream baos = new Jar()
.setAttribute("Foo", "1234")
.setAttribute("Bar", "5678")
.addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.write(new ByteArrayOutputStream());
// update
ByteArrayOutputStream res = new Jar(toInput(baos))
.setAttribute("Baz", "hi!")
.setAttribute("Bar", "8765")
.addEntry(Paths.get("dir", "baz.txt"), Jar.toInputStream("And I am baz!\n", UTF_8))
.write(new ByteArrayOutputStream());
// test
// printEntries(toInput(res));
assertEquals("I am foo!\n", getEntryAsString(toInput(res), Paths.get("foo.txt"), UTF_8));
assertEquals("I am bar!\n", getEntryAsString(toInput(res), Paths.get("dir", "bar.txt"), UTF_8));
assertEquals("And I am baz!\n", getEntryAsString(toInput(res), Paths.get("dir", "baz.txt"), UTF_8));
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
assertEquals("8765", man2.getMainAttributes().getValue("Bar"));
assertEquals("hi!", man2.getMainAttributes().getValue("Baz"));
}
@Test
public void testUpdateJar3() throws Exception {
// create
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new Jar()
.setOutputStream(baos)
.setAttribute("Foo", "1234")
.setAttribute("Bar", "5678")
.addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.close();
// update
ByteArrayOutputStream res = new Jar(toInput(baos))
.setAttribute("Baz", "hi!")
.setAttribute("Bar", "8765")
.addEntry(Paths.get("dir", "baz.txt"), Jar.toInputStream("And I am baz!\n", UTF_8))
.write(new ByteArrayOutputStream());
// test
// printEntries(toInput(res));
assertEquals("I am foo!\n", getEntryAsString(toInput(res), Paths.get("foo.txt"), UTF_8));
assertEquals("I am bar!\n", getEntryAsString(toInput(res), Paths.get("dir", "bar.txt"), UTF_8));
assertEquals("And I am baz!\n", getEntryAsString(toInput(res), Paths.get("dir", "baz.txt"), UTF_8));
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
assertEquals("8765", man2.getMainAttributes().getValue("Bar"));
assertEquals("hi!", man2.getMainAttributes().getValue("Baz"));
}
@Test
public void testAddDirectory1() throws Exception {
FileSystem fs = Jimfs.newFileSystem();
Path myDir = fs.getPath("dir1", "dir2");
Files.createDirectories(myDir.resolve("da"));
Files.createDirectories(myDir.resolve("db"));
Files.createFile(myDir.resolve("da").resolve("x"));
Files.createFile(myDir.resolve("da").resolve("y"));
Files.createFile(myDir.resolve("db").resolve("w"));
Files.createFile(myDir.resolve("db").resolve("z"));
ByteArrayOutputStream res = new Jar()
.setAttribute("Foo", "1234")
.addEntries((Path) null, myDir, Jar.notMatches("db/w"))
.write(new ByteArrayOutputStream());
// printEntries(toInput(res));
assertTrue(getEntry(toInput(res), Paths.get("da", "x")) != null);
assertTrue(getEntry(toInput(res), Paths.get("da", "y")) != null);
assertTrue(getEntry(toInput(res), Paths.get("db", "w")) == null);
assertTrue(getEntry(toInput(res), Paths.get("db", "z")) != null);
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
}
@Test
public void testAddDirectory2() throws Exception {
FileSystem fs = Jimfs.newFileSystem();
Path myDir = fs.getPath("dir1", "dir2");
Files.createDirectories(myDir.resolve("da"));
Files.createDirectories(myDir.resolve("db"));
Files.createFile(myDir.resolve("da").resolve("x"));
Files.createFile(myDir.resolve("da").resolve("y"));
Files.createFile(myDir.resolve("db").resolve("w"));
Files.createFile(myDir.resolve("db").resolve("z"));
ByteArrayOutputStream res = new Jar()
.setAttribute("Foo", "1234")
.addEntries(Paths.get("d1", "d2"), myDir, Jar.notMatches("d1/d2/db/w"))
.write(new ByteArrayOutputStream());
// printEntries(toInput(res));
assertTrue(getEntry(toInput(res), Paths.get("d1", "d2", "da", "x")) != null);
assertTrue(getEntry(toInput(res), Paths.get("d1", "d2", "da", "y")) != null);
assertTrue(getEntry(toInput(res), Paths.get("d1", "d2", "db", "w")) == null);
assertTrue(getEntry(toInput(res), Paths.get("d1", "d2", "db", "z")) != null);
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
}
@Test
public void testAddZip1() throws Exception {
FileSystem fs = Jimfs.newFileSystem();
Path myZip = fs.getPath("zip1");
new Jar().addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.write(myZip);
ByteArrayOutputStream res = new Jar()
.setAttribute("Foo", "1234")
.addEntries((String) null, myZip)
.write(new ByteArrayOutputStream());
// printEntries(toInput(res));
assertEquals("I am foo!\n", getEntryAsString(toInput(res), Paths.get("foo.txt"), UTF_8));
assertEquals("I am bar!\n", getEntryAsString(toInput(res), Paths.get("dir", "bar.txt"), UTF_8));
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
}
@Test
public void testAddZip2() throws Exception {
FileSystem fs = Jimfs.newFileSystem();
Path myZip = fs.getPath("zip1");
new Jar().addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.write(myZip);
ByteArrayOutputStream res = new Jar()
.setAttribute("Foo", "1234")
.addEntries(Paths.get("d1", "d2"), myZip)
.write(new ByteArrayOutputStream());
// printEntries(toInput(res));
assertEquals("I am foo!\n", getEntryAsString(toInput(res), Paths.get("d1", "d2", "foo.txt"), UTF_8));
assertEquals("I am bar!\n", getEntryAsString(toInput(res), Paths.get("d1", "d2", "dir", "bar.txt"), UTF_8));
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
}
@Test
public void testAddZip3() throws Exception {
ByteArrayOutputStream myZip = new Jar()
.addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.write(new ByteArrayOutputStream());
ByteArrayOutputStream res = new Jar()
.setAttribute("Foo", "1234")
.addEntries((Path) null, toInput(myZip))
.write(new ByteArrayOutputStream());
// printEntries(toInput(res));
assertEquals("I am foo!\n", getEntryAsString(toInput(res), Paths.get("foo.txt"), UTF_8));
assertEquals("I am bar!\n", getEntryAsString(toInput(res), Paths.get("dir", "bar.txt"), UTF_8));
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
}
@Test
public void testAddZip4() throws Exception {
ByteArrayOutputStream myZip = new Jar()
.addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.write(new ByteArrayOutputStream());
ByteArrayOutputStream res = new Jar()
.setAttribute("Foo", "1234")
.addEntries(Paths.get("d1", "d2"), toInput(myZip))
.write(new ByteArrayOutputStream());
// printEntries(toInput(res));
assertEquals("I am foo!\n", getEntryAsString(toInput(res), Paths.get("d1", "d2", "foo.txt"), UTF_8));
assertEquals("I am bar!\n", getEntryAsString(toInput(res), Paths.get("d1", "d2", "dir", "bar.txt"), UTF_8));
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
}
@Test
public void testAddPackage() throws Exception {
final Class clazz = JarClassLoader.class;
ByteArrayOutputStream res = new Jar()
.setAttribute("Foo", "1234")
.addPackageOf(clazz, new Jar.Filter() {
@Override
public boolean filter(String entryName) {
return !"co/paralleluniverse/common/FlexibleClassLoader.class".equals(entryName);
}
})
.write(new ByteArrayOutputStream());
// printEntries(toInput(res));
final Path pp = Paths.get(clazz.getPackage().getName().replace('.', '/'));
assertTrue(getEntry(toInput(res), pp.resolve(clazz.getSimpleName() + ".class")) != null);
assertTrue(getEntry(toInput(res), pp.resolve("ProcessUtil.class")) != null);
assertTrue(getEntry(toInput(res), pp.resolve("FlexibleClassLoader.class")) == null);
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
}
@Test
public void testAddPackage2() throws Exception {
FileSystem fs = Jimfs.newFileSystem();
Path jarPath = fs.getPath("test.jar");
final Class clazz = JarClassLoader.class;
new Jar().addPackageOf(clazz).write(jarPath);
ByteArrayOutputStream res = new Jar()
.setAttribute("Foo", "1234")
.addPackageOf(new JarClassLoader(jarPath, true).loadClass(clazz.getName()), new Jar.Filter() {
@Override
public boolean filter(String entryName) {
return !"co/paralleluniverse/common/FlexibleClassLoader.class".equals(entryName);
}
})
.write(new ByteArrayOutputStream());
// printEntries(toInput(res));
final Path pp = Paths.get(clazz.getPackage().getName().replace('.', '/'));
assertTrue(getEntry(toInput(res), pp.resolve(clazz.getSimpleName() + ".class")) != null);
assertTrue(getEntry(toInput(res), pp.resolve("ProcessUtil.class")) != null);
assertTrue(getEntry(toInput(res), pp.resolve("FlexibleClassLoader.class")) == null);
Manifest man2 = toInput(res).getManifest();
assertEquals("1234", man2.getMainAttributes().getValue("Foo"));
}
@Test
public void testReallyExecutableJar() throws Exception {
FileSystem fs = Jimfs.newFileSystem();
Path jarPath = fs.getPath("test.jar");
new Jar()
.setAttribute("Foo", "1234")
.setAttribute("Bar", "5678")
.setReallyExecutable(true)
.addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.write(jarPath);
// printEntries(toInput(res));
BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(jarPath), UTF_8), 10); // Files.newBufferedReader(jarPath, UTF_8);
String firstLine = reader.readLine();
assertEquals("#!/bin/sh", firstLine);
}
@Test
public void testStringPrefix() throws Exception {
FileSystem fs = Jimfs.newFileSystem();
Path jarPath = fs.getPath("test.jar");
new Jar()
.setAttribute("Foo", "1234")
.setAttribute("Bar", "5678")
.setJarPrefix("I'm the prefix!")
.addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.write(jarPath);
// printEntries(toInput(res));
BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(jarPath), UTF_8), 10); // Files.newBufferedReader(jarPath, UTF_8);
String firstLine = reader.readLine();
assertEquals("I'm the prefix!", firstLine);
}
@Test
public void testFilePrefix() throws Exception {
FileSystem fs = Jimfs.newFileSystem();
Path jarPath = fs.getPath("test.jar");
Path prefixPath = fs.getPath("prefix.dat");
Files.copy(toInputStream("I'm the prefix!", UTF_8), prefixPath);
new Jar()
.setAttribute("Foo", "1234")
.setAttribute("Bar", "5678")
.setJarPrefix(prefixPath)
.addEntry(Paths.get("foo.txt"), Jar.toInputStream("I am foo!\n", UTF_8))
.addEntry(Paths.get("dir", "bar.txt"), Jar.toInputStream("I am bar!\n", UTF_8))
.write(jarPath);
// printEntries(toInput(res));
BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(jarPath), UTF_8), 10); // Files.newBufferedReader(jarPath, UTF_8);
String firstLine = reader.readLine();
assertEquals("I'm the prefix!", firstLine);
}
//<editor-fold defaultstate="collapsed" desc="Utilities">
/////////// Utilities ///////////////////////////////////
private static JarInputStream toInput(JarOutputStream jos) {
try {
Field outField = FilterOutputStream.class.getDeclaredField("out");
outField.setAccessible(true);
jos.close();
return toInput((ByteArrayOutputStream) outField.get(jos));
} catch (Exception e) {
throw new AssertionError(e);
}
}
private static JarInputStream toInput(ByteArrayOutputStream baos) {
try {
baos.close();
byte[] bytes = baos.toByteArray();
return new JarInputStream(new ByteArrayInputStream(bytes));
} catch (Exception e) {
throw new AssertionError(e);
}
}
private static <T> List<T> addLast(List<T> list, T value) {
list.add(value);
return list;
}
private static <T> List<T> addFirst(List<T> list, T value) {
list.add(0, value);
return list;
}
private static <K, V> Map<K, V> put(Map<K, V> map, K key, V value) {
map.put(key, value);
return map;
}
private static String getEntryAsString(JarInputStream jar, Path entry, Charset charset) throws IOException {
byte[] buffer = getEntry(jar, entry);
return buffer != null ? new String(buffer, charset) : null;
}
private static String getEntryAsString(JarFile jar, Path entry, Charset charset) throws IOException {
byte[] buffer = getEntry(jar, entry);
return buffer != null ? new String(buffer, charset) : null;
}
private static String getEntryAsString(Path jar, Path entry, Charset charset) throws IOException {
byte[] buffer = getEntry(jar, entry);
return buffer != null ? new String(buffer, charset) : null;
}
private static byte[] getEntry(Path jar, Path entry) throws IOException {
try (FileSystem zipfs = ZipFS.newZipFileSystem(jar)) {
return Files.readAllBytes(zipfs.getPath(entry.toString()));
}
}
private static InputStream toInputStream(String s, Charset charset) {
return new ByteArrayInputStream(s.getBytes(charset));
}
private static Manifest getManifest(Path jar, boolean useZipfs) throws IOException {
if (useZipfs) {
try (FileSystem zipfs = ZipFS.newZipFileSystem(jar)) {
return new Manifest(Files.newInputStream(zipfs.getPath(JarFile.MANIFEST_NAME)));
}
} else
return new JarInputStream(Files.newInputStream(jar)).getManifest();
}
private static byte[] getEntry(JarFile jar, Path entry) throws IOException {
final Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
final JarEntry je = entries.nextElement();
if (je.getName().equals(entry.toString())) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(jar.getInputStream(je), baos);
baos.close();
return baos.toByteArray();
}
}
return null;
}
private static byte[] getEntry(JarInputStream jar, Path entry) throws IOException {
JarEntry je;
while ((je = jar.getNextJarEntry()) != null) {
if (je.getName().equals(entry.toString())) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(jar, baos);
baos.close();
return baos.toByteArray();
}
}
return null;
}
private static void printEntries(JarInputStream jar) throws IOException {
JarEntry je;
while ((je = jar.getNextJarEntry()) != null)
System.out.println(je.getName());
System.out.println();
}
private static void copy(InputStream is, OutputStream os) throws IOException {
try {
final byte[] buffer = new byte[1024];
for (int bytesRead; (bytesRead = is.read(buffer)) != -1;)
os.write(buffer, 0, bytesRead);
} finally {
is.close();
}
}
//</editor-fold>
}