/*
* 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.common;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Enumeration;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
/**
*
* @author pron
*/
public final class JarClassLoader extends FlexibleClassLoader {
private final Manifest mf;
private final byte[] buffer;
private final Path jarFile;
public JarClassLoader(byte[] jar, ClassLoader parent, boolean childFirst) throws IOException {
super(parent, childFirst);
this.buffer = jar;
this.jarFile = null;
this.mf = getManifest(newInputStream());
}
public JarClassLoader(byte[] jar, boolean childFirst) throws IOException {
super(childFirst);
this.buffer = jar;
this.jarFile = null;
this.mf = getManifest(newInputStream());
}
public JarClassLoader(Path jarFile, ClassLoader parent, boolean childFirst) throws IOException {
super(parent, childFirst);
this.buffer = null;
this.jarFile = jarFile;
this.mf = getManifest(newInputStream());
}
public JarClassLoader(Path jarFile, boolean childFirst) throws IOException {
super(childFirst);
this.buffer = null;
this.jarFile = jarFile;
this.mf = getManifest(newInputStream());
}
public Manifest getManifest() {
return mf;
}
@Override
protected URL findResource1(String name) {
try {
if (!hasResource(name))
return null;
else
return new URL("jar:" + jarFile.toUri() + "!/" + name);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
@Override
@SuppressWarnings("unchecked")
protected Enumeration<URL> findResources1(String name) {
final URL url = findResource1(name);
return (Enumeration<URL>) Collections.enumeration(url != null ? Collections.singleton(url) : Collections.emptySet());
}
private boolean hasResource(String path) {
try (InputStream is = findResourceAsStream(path)) {
return is != null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected InputStream findResourceAsStream(String path) {
try {
final ZipInputStream jis = new ZipInputStream(newInputStream());
for (ZipEntry entry; (entry = jis.getNextEntry()) != null;) {
if (path.equalsIgnoreCase(entry.getName())) {
if (entry.isDirectory())
throw new FileNotFoundException(path + " is a directory");
return jis;
}
}
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected byte[] readResource(String path) {
try (ZipInputStream jis = new ZipInputStream(newInputStream())) {
for (ZipEntry entry; (entry = jis.getNextEntry()) != null;) {
if (path.equalsIgnoreCase(entry.getName())) {
if (entry.isDirectory())
throw new FileNotFoundException(path + " is a directory");
final byte[] buf;
final long size = entry.getSize();
if (size < 0) {
final ByteArrayOutputStream bas = new ByteArrayOutputStream();
copy(jis, bas);
bas.close();
buf = bas.toByteArray();
} else {
buf = new byte[(int) size];
int n = 0, r;
for (;;) {
if (n == size || (r = jis.read(buf, n, buf.length - n)) == -1)
break;
n += r;
}
}
return buf;
}
}
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static long copy(InputStream from, OutputStream to) throws IOException {
byte[] buf = new byte[0x1000]; // 4K
long total = 0;
while (true) {
int r = from.read(buf);
if (r == -1) {
break;
}
to.write(buf, 0, r);
total += r;
}
return total;
}
private InputStream newInputStream() {
try {
return buffer != null ? new ByteArrayInputStream(buffer) : Files.newInputStream(jarFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static Manifest getManifest(InputStream is) throws IOException {
try (JarInputStream jis = new JarInputStream(is)) {
return jis.getManifest();
}
}
@Override
public String toString() {
return super.toString() + '{' + jarFile.toAbsolutePath() + '}';
}
}