From 8ea226849106f7b05ba3383fc4cf901058d94557 Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Thu, 1 Jul 2021 22:21:26 +0100 Subject: [PATCH] added PathClassLoaderTest --- build.gradle | 15 +++- gradle.properties | 1 + .../woggioni/jwo/loader/PathClassLoader.java | 82 ++++++++++--------- .../jwo/loader/PathClassLoaderTest.java | 48 +++++++++++ 4 files changed, 106 insertions(+), 40 deletions(-) create mode 100644 src/test/java/net/woggioni/jwo/loader/PathClassLoaderTest.java diff --git a/build.gradle b/build.gradle index e7ec176..4764598 100644 --- a/build.gradle +++ b/build.gradle @@ -26,8 +26,14 @@ allprojects { } } + +configurations { + pathClassloaderTest +} + dependencies { implementation group: "org.slf4j", name: "slf4j-api", version: getProperty('slf4j.version') + pathClassloaderTest group: 'com.google.inject', name: 'guice', version: getProperty('guice.version') } compileJava { @@ -41,8 +47,13 @@ jar { ]) } } +TaskProvider pathClassLoaderTestBundleTask = tasks.register("pathClassLoaderTestBundle", Zip) { + from(configurations.pathClassloaderTest) + archiveBaseName = "pathClassLoaderTestBundle" +} test { + inputs.files(pathClassLoaderTestBundleTask) useJUnitPlatform() Dependency junitJupiterEngineDependency = dependencies.create( @@ -59,7 +70,8 @@ test { ResolvedArtifact resolvedArtifact -> resolvedArtifact.file }.first() 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 { } + diff --git a/gradle.properties b/gradle.properties index bad4451..80e1eda 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,3 +3,4 @@ jwo.version=1.0 junitJupiter.version=5.7.0 lombok.version=1.18.16 slf4j.version=1.7.30 +guice.version = 5.0.1 diff --git a/src/main/java/net/woggioni/jwo/loader/PathClassLoader.java b/src/main/java/net/woggioni/jwo/loader/PathClassLoader.java index d5d3416..f3bc3d0 100644 --- a/src/main/java/net/woggioni/jwo/loader/PathClassLoader.java +++ b/src/main/java/net/woggioni/jwo/loader/PathClassLoader.java @@ -9,81 +9,85 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; +import java.util.*; /** * A classloader that loads classes from a {@link Path} instance */ public final class PathClassLoader extends ClassLoader { - private final Path path; + private final Iterable paths; static { registerAsParallelCapable(); } - public PathClassLoader(Path path) { - this(path, null); + public PathClassLoader(Path ...path) { + this(Arrays.asList(path), null); } - public PathClassLoader(Path path, ClassLoader parent) { + public PathClassLoader(Iterable paths) { + this(paths, null); + } + + public PathClassLoader(Iterable paths, ClassLoader parent) { super(parent); - this.path = path; + this.paths = paths; } @Override @SneakyThrows protected Class findClass(String name) { - Path classPath = path.resolve(name.replace('.', '/').concat(".class")); - if (Files.exists(classPath)) { - byte[] byteCode = Files.readAllBytes(classPath); - return defineClass(name, byteCode, 0, byteCode.length); - } else { - throw new ClassNotFoundException(name); + for(Path path : paths) { + Path classPath = path.resolve(name.replace('.', '/').concat(".class")); + if (Files.exists(classPath)) { + byte[] byteCode = Files.readAllBytes(classPath); + return defineClass(name, byteCode, 0, byteCode.length); + } } + throw new ClassNotFoundException(name); } @Override @SneakyThrows protected URL findResource(String name) { - Path resolved = path.resolve(name); - if (Files.exists(resolved)) { - return toURL(resolved); - } else { - return null; + for(Path path : paths) { + Path resolved = path.resolve(name); + if (Files.exists(resolved)) { + return toURL(resolved); + } } + return null; } @Override protected Enumeration findResources(final String name) throws IOException { final List resources = new ArrayList<>(1); - - Files.walkFileTree(path, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (!name.isEmpty()) { - this.addIfMatches(resources, file); + for(Path path : paths) { + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (!name.isEmpty()) { + this.addIfMatches(resources, file); + } + return super.visitFile(file, attrs); } - return super.visitFile(file, attrs); - } - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (!name.isEmpty() || path.equals(dir)) { - this.addIfMatches(resources, dir); + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + if (!name.isEmpty() || path.equals(dir)) { + this.addIfMatches(resources, dir); + } + return super.preVisitDirectory(dir, attrs); } - return super.preVisitDirectory(dir, attrs); - } - void addIfMatches(List resources, Path file) throws IOException { - if (path.relativize(file).toString().equals(name)) { - resources.add(toURL(file)); + void addIfMatches(List resources, Path file) throws IOException { + if (path.relativize(file).toString().equals(name)) { + resources.add(toURL(file)); + } } - } - }); + }); + } return Collections.enumeration(resources); } diff --git a/src/test/java/net/woggioni/jwo/loader/PathClassLoaderTest.java b/src/test/java/net/woggioni/jwo/loader/PathClassLoaderTest.java new file mode 100644 index 0000000..2ef66e3 --- /dev/null +++ b/src/test/java/net/woggioni/jwo/loader/PathClassLoaderTest.java @@ -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 paths = StreamSupport.stream(fs.getRootDirectories().spliterator(), false).flatMap(new Function>() { + @Override + @SneakyThrows + public Stream apply(Path path) { + return Files.list(path) + .filter(Files::isRegularFile) + .filter(p -> p.getFileName().toString().endsWith(".jar")); + } + }).flatMap(new Function>() { + @Override + @SneakyThrows + public Stream 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]); + } +}