Support java.nio.file Paths and FileSystems with nested jars

Add a `NestedFileSystemProvider` implementation so that the JDK's
`ZipFileSystem` can load content from nested jars and nested
directory entries.

Creating a `ZipFileSystem` may be a relatively expensive operation as
zip structures need to be parsed and in the case of directory entries
a virtual datablock nees to be generated on the fly. As such, we
install the `ZipFileSystem` as late as possible since in a typical
application it may never be needed.

This commit also tweaks Gradle and Maven plugins to ensure that the
service loader file is written to repackaged jars.

Closes gh-7161
This commit is contained in:
Phillip Webb
2023-10-18 14:57:05 -07:00
parent 4b495ca2a9
commit 3c62defb9d
21 changed files with 2115 additions and 5 deletions

View File

@@ -1,6 +1,8 @@
plugins {
id "java"
id "org.springframework.boot"
// id 'org.springframework.boot' version '3.1.4'
// id 'io.spring.dependency-management' version '1.1.3'
}
apply plugin: "io.spring.dependency-management"

View File

@@ -17,8 +17,13 @@
package org.springframework.boot.loaderapp;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import jakarta.servlet.ServletContext;
@@ -27,6 +32,8 @@ import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.FileCopyUtils;
@SpringBootApplication
@@ -49,9 +56,20 @@ public class LoaderTestApplication {
String message = (!Arrays.equals(resourceContent, directContent)) ? "NO MATCH"
: directContent.length + " BYTES";
System.out.println(">>>>> " + message + " from " + resourceUrl);
testGh7161();
};
}
private void testGh7161() {
try {
Resource resource = new ClassPathResource("gh-7161");
Path path = Paths.get(resource.getURI());
System.out.println(">>>>> gh-7161 " + Files.list(path).toList());
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
public static void main(String[] args) {
SpringApplication.run(LoaderTestApplication.class, args).close();
}

View File

@@ -51,11 +51,12 @@ class LoaderIntegrationTests {
@ParameterizedTest
@MethodSource("javaRuntimes")
void readUrlsWithoutWarning(JavaRuntime javaRuntime) {
void runJar(JavaRuntime javaRuntime) {
try (GenericContainer<?> container = createContainer(javaRuntime, "spring-boot-loader-tests-app", null)) {
container.start();
System.out.println(this.output.toUtf8String());
assertThat(this.output.toUtf8String()).contains(">>>>> 287649 BYTES from")
.contains(">>>>> gh-7161 [/gh-7161/example.txt]")
.doesNotContain("WARNING:")
.doesNotContain("illegal")
.doesNotContain("jar written to temp");