102 lines
3.1 KiB
Java
102 lines
3.1 KiB
Java
package io.gitlab.jfronny.inceptum.wrapper;
|
|
|
|
import io.gitlab.jfronny.commons.io.JFiles;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
import java.io.Closeable;
|
|
import java.io.IOException;
|
|
import java.net.*;
|
|
import java.nio.file.*;
|
|
import java.security.CodeSource;
|
|
import java.security.ProtectionDomain;
|
|
import java.security.cert.Certificate;
|
|
import java.util.*;
|
|
import java.util.stream.Stream;
|
|
|
|
/**
|
|
* A ClassLoader that supports loading classes from Paths, which may be directories or jars
|
|
* Does not support modules
|
|
*/
|
|
public class NioClassLoader extends ClassLoader implements Closeable {
|
|
private final Set<Path> sources = new LinkedHashSet<>();
|
|
private final Set<Closeable> closeables = new HashSet<>();
|
|
|
|
static {
|
|
registerAsParallelCapable();
|
|
}
|
|
|
|
public NioClassLoader(Iterable<Path> jars, ClassLoader parent) throws IOException, URISyntaxException {
|
|
super(parent);
|
|
for (Path jar : jars) {
|
|
if (Files.isDirectory(jar)) {
|
|
sources.add(jar);
|
|
} else {
|
|
FileSystem fs = JFiles.openZipFile(jar, false, parent);
|
|
closeables.add(fs);
|
|
sources.add(fs.getPath(""));
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
|
Path path = findSource(name.replace('.', '/') + ".class");
|
|
if (path == null) throw new ClassNotFoundException(name);
|
|
byte[] clazz;
|
|
try {
|
|
clazz = Files.readAllBytes(path);
|
|
} catch (IOException e) {
|
|
throw new ClassNotFoundException(name, e);
|
|
}
|
|
CodeSource cs = new CodeSource(toUrl(path), (Certificate[]) null);
|
|
ProtectionDomain pd = new ProtectionDomain(cs, null);
|
|
return defineClass(name, clazz, 0, clazz.length, pd);
|
|
}
|
|
|
|
@Override
|
|
protected URL findResource(String name) {
|
|
return toUrl(findSource(name));
|
|
}
|
|
|
|
@Override
|
|
protected Enumeration<URL> findResources(String name) {
|
|
return Collections.enumeration(findPaths(name).map(this::toUrl).toList());
|
|
}
|
|
|
|
private URL toUrl(Path path) {
|
|
if (path == null) return null;
|
|
try {
|
|
return path.toUri().toURL();
|
|
} catch (MalformedURLException e) {
|
|
throw new RuntimeException("Could not get resource", e);
|
|
}
|
|
}
|
|
|
|
private @Nullable Path findSource(String path) {
|
|
return findPaths(path).findFirst().orElse(null);
|
|
}
|
|
|
|
private Stream<Path> findPaths(String path) {
|
|
return sources.stream().map(s -> s.resolve(path)).filter(Files::exists);
|
|
}
|
|
|
|
@Override
|
|
public void close() {
|
|
List<IOException> exceptions = new LinkedList<>();
|
|
for (Closeable closeable : closeables) {
|
|
try {
|
|
closeable.close();
|
|
} catch (IOException e) {
|
|
exceptions.add(e);
|
|
}
|
|
}
|
|
if (!exceptions.isEmpty()) {
|
|
RuntimeException ex = new RuntimeException("Could not properly close WrapperClassLoader");
|
|
for (IOException suppressed : exceptions) {
|
|
ex.addSuppressed(suppressed);
|
|
}
|
|
throw ex;
|
|
}
|
|
}
|
|
}
|