added PathClassLoaderTest

This commit is contained in:
2021-07-01 22:21:26 +01:00
parent 299367fb74
commit 8ea2268491
4 changed files with 106 additions and 40 deletions

View File

@@ -26,8 +26,14 @@ allprojects {
} }
} }
configurations {
pathClassloaderTest
}
dependencies { dependencies {
implementation group: "org.slf4j", name: "slf4j-api", version: getProperty('slf4j.version') implementation group: "org.slf4j", name: "slf4j-api", version: getProperty('slf4j.version')
pathClassloaderTest group: 'com.google.inject', name: 'guice', version: getProperty('guice.version')
} }
compileJava { compileJava {
@@ -41,8 +47,13 @@ jar {
]) ])
} }
} }
TaskProvider<Zip> pathClassLoaderTestBundleTask = tasks.register("pathClassLoaderTestBundle", Zip) {
from(configurations.pathClassloaderTest)
archiveBaseName = "pathClassLoaderTestBundle"
}
test { test {
inputs.files(pathClassLoaderTestBundleTask)
useJUnitPlatform() useJUnitPlatform()
Dependency junitJupiterEngineDependency = Dependency junitJupiterEngineDependency =
dependencies.create( dependencies.create(
@@ -59,7 +70,8 @@ test {
ResolvedArtifact resolvedArtifact -> resolvedArtifact.file ResolvedArtifact resolvedArtifact -> resolvedArtifact.file
}.first() }.first()
systemProperties([ systemProperties([
'junit.jupiter.engine.jar' : junitJupiterEngineJar.toString() 'junit.jupiter.engine.jar' : junitJupiterEngineJar.toString(),
'path.classloader.test.bundle' : pathClassLoaderTestBundleTask.get().outputs.files.singleFile
]) ])
} }
@@ -82,3 +94,4 @@ publishing {
} }

View File

@@ -3,3 +3,4 @@ jwo.version=1.0
junitJupiter.version=5.7.0 junitJupiter.version=5.7.0
lombok.version=1.18.16 lombok.version=1.18.16
slf4j.version=1.7.30 slf4j.version=1.7.30
guice.version = 5.0.1

View File

@@ -9,81 +9,85 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor; import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
/** /**
* A classloader that loads classes from a {@link Path} instance * A classloader that loads classes from a {@link Path} instance
*/ */
public final class PathClassLoader extends ClassLoader { public final class PathClassLoader extends ClassLoader {
private final Path path; private final Iterable<Path> paths;
static { static {
registerAsParallelCapable(); registerAsParallelCapable();
} }
public PathClassLoader(Path path) { public PathClassLoader(Path ...path) {
this(path, null); this(Arrays.asList(path), null);
} }
public PathClassLoader(Path path, ClassLoader parent) { public PathClassLoader(Iterable<Path> paths) {
this(paths, null);
}
public PathClassLoader(Iterable<Path> paths, ClassLoader parent) {
super(parent); super(parent);
this.path = path; this.paths = paths;
} }
@Override @Override
@SneakyThrows @SneakyThrows
protected Class<?> findClass(String name) { protected Class<?> findClass(String name) {
Path classPath = path.resolve(name.replace('.', '/').concat(".class")); for(Path path : paths) {
if (Files.exists(classPath)) { Path classPath = path.resolve(name.replace('.', '/').concat(".class"));
byte[] byteCode = Files.readAllBytes(classPath); if (Files.exists(classPath)) {
return defineClass(name, byteCode, 0, byteCode.length); byte[] byteCode = Files.readAllBytes(classPath);
} else { return defineClass(name, byteCode, 0, byteCode.length);
throw new ClassNotFoundException(name); }
} }
throw new ClassNotFoundException(name);
} }
@Override @Override
@SneakyThrows @SneakyThrows
protected URL findResource(String name) { protected URL findResource(String name) {
Path resolved = path.resolve(name); for(Path path : paths) {
if (Files.exists(resolved)) { Path resolved = path.resolve(name);
return toURL(resolved); if (Files.exists(resolved)) {
} else { return toURL(resolved);
return null; }
} }
return null;
} }
@Override @Override
protected Enumeration<URL> findResources(final String name) throws IOException { protected Enumeration<URL> findResources(final String name) throws IOException {
final List<URL> resources = new ArrayList<>(1); final List<URL> resources = new ArrayList<>(1);
for(Path path : paths) {
Files.walkFileTree(path, new SimpleFileVisitor<Path>() { Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (!name.isEmpty()) { if (!name.isEmpty()) {
this.addIfMatches(resources, file); this.addIfMatches(resources, file);
}
return super.visitFile(file, attrs);
} }
return super.visitFile(file, attrs);
}
@Override @Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
if (!name.isEmpty() || path.equals(dir)) { if (!name.isEmpty() || path.equals(dir)) {
this.addIfMatches(resources, dir); this.addIfMatches(resources, dir);
}
return super.preVisitDirectory(dir, attrs);
} }
return super.preVisitDirectory(dir, attrs);
}
void addIfMatches(List<URL> resources, Path file) throws IOException { void addIfMatches(List<URL> resources, Path file) throws IOException {
if (path.relativize(file).toString().equals(name)) { if (path.relativize(file).toString().equals(name)) {
resources.add(toURL(file)); resources.add(toURL(file));
}
} }
} });
}); }
return Collections.enumeration(resources); return Collections.enumeration(resources);
} }

View File

@@ -0,0 +1,48 @@
package net.woggioni.jwo.loader;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class PathClassLoaderTest {
Path testBundle = Path.of(System.getProperty("path.classloader.test.bundle"));
@Test
@SneakyThrows
void test() {
FileSystem fs = FileSystems.newFileSystem(testBundle, null);
List<Path> paths = StreamSupport.stream(fs.getRootDirectories().spliterator(), false).flatMap(new Function<Path, Stream<Path>>() {
@Override
@SneakyThrows
public Stream<Path> apply(Path path) {
return Files.list(path)
.filter(Files::isRegularFile)
.filter(p -> p.getFileName().toString().endsWith(".jar"));
}
}).flatMap(new Function<Path, Stream<Path>>() {
@Override
@SneakyThrows
public Stream<Path> apply(Path path) {
System.out.println(path.getFileName().toString());
return StreamSupport.stream(FileSystems.newFileSystem(path, null).getRootDirectories().spliterator(), false);
}
}).collect(Collectors.toUnmodifiableList());
PathClassLoader classLoader = new PathClassLoader(paths);
Class<?>[] cls = new Class[1];
Assertions.assertDoesNotThrow(() -> {
cls[0] = classLoader.loadClass("com.google.common.collect.ImmutableMap");
});
Assertions.assertNotNull(cls[0]);
}
}