diff --git a/gh-8822/build.gradle b/gh-8822/build.gradle
new file mode 100644
index 0000000..63f9921
--- /dev/null
+++ b/gh-8822/build.gradle
@@ -0,0 +1,42 @@
+apply plugin: 'org.springframework.boot'
+
+buildscript {
+ ext {
+ springBootVersion = '1.5.2.RELEASE'
+ }
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ // http://mvnrepository.com/artifact/org.springframework.boot/spring-boot-gradle-plugin
+ classpath group: 'org.springframework.boot', name: 'spring-boot-gradle-plugin', version: springBootVersion
+ }
+}
+
+wrapper {
+ gradleVersion = '3.4.1'
+ distributionType = Wrapper.DistributionType.ALL
+}
+
+repositories.addAll rootProject.buildscript.repositories
+
+subprojects {
+ repositories.addAll rootProject.buildscript.repositories
+ buildscript.repositories.addAll rootProject.buildscript.repositories
+}
+
+dependencies {
+ // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-loader
+ // should be runtime but JavaDoc @see requires compile for validation
+ compile group: 'org.springframework.boot', name: 'spring-boot-loader', version: springBootVersion
+}
+
+bootRepackage {
+ // https://github.com/spring-projects/spring-boot/issues/1113
+ // https://github.com/spring-projects/spring-boot/issues/8167
+ classifier = 'repack'
+}
+
+jar {
+ manifest.attributes 'Main-Class': 'Main'
+}
diff --git a/gh-8822/src/main/java/Main.java b/gh-8822/src/main/java/Main.java
new file mode 100644
index 0000000..f272f56
--- /dev/null
+++ b/gh-8822/src/main/java/Main.java
@@ -0,0 +1,94 @@
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+
+/**
+ * @see org.springframework.boot.loader.JarLauncher#main(java.lang.String[])
+ * @see org.springframework.boot.loader.Launcher#launch(java.lang.String[])
+ * @see org.springframework.boot.loader.ExecutableArchiveLauncher#getClassPathArchives()
+ * @see org.springframework.boot.loader.archive.JarFileArchive#getNestedArchives(org.springframework.boot.loader.archive.Archive.EntryFilter)
+ * @see org.springframework.boot.loader.archive.JarFileArchive#getNestedArchive(org.springframework.boot.loader.archive.Archive.Entry)
+ * @see org.springframework.boot.loader.jar.JarFile#getNestedJarFile(java.util.zip.ZipEntry)
+ * @see org.springframework.boot.loader.jar.JarFile#getNestedJarFile(org.springframework.boot.loader.jar.JarEntry)
+ * @see org.springframework.boot.loader.jar.JarFile#createJarFileFromEntry(org.springframework.boot.loader.jar.JarEntry)
+ * @see Jar launcer adds ! after BOOT-INF/classes url for resource
+ * @see org.springframework.boot.loader.LaunchedURLClassLoader unable to load 3rd party FileSystemProvider implementation from spring boot executable JAR
+ */
+public class Main {
+ public static void main(String[] args) throws URISyntaxException, IOException {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ printClassLoaderTree(classLoader);
+
+ // get resourceUri
+ URL resourceUrl = classLoader.getResource("test.txt");
+ assert resourceUrl != null;
+ URI resourceUri = resourceUrl.toURI();
+
+ // open jar if necessary
+ if (resourceUri.toString().startsWith("jar:")) {
+ System.out.println("\nOpening jar…");
+ FileSystems.newFileSystem(resourceUri, Collections.emptyMap());
+ }
+
+ System.out.println("");
+
+ // test files exists
+ {
+ Path resourcePath = Paths.get(resourceUri);
+ boolean exists = Files.exists(resourcePath);
+ System.out.println(
+ "Files.exists(resourcePath): " + exists
+ + "\n resourcePath: " + resourcePath
+ + "\n resourceUri: " + resourceUri
+ );
+ }
+
+ // apply workaround
+ {
+ System.out.println("\nApplying workaround. Replacing '!/BOOT-INF/classes!/' with '!/BOOT-INF/classes/'.\n");
+ String brokenUri = resourceUri.toString();
+ String fixedUri = brokenUri.replace("!/BOOT-INF/classes!/", "!/BOOT-INF/classes/");
+ resourceUri = new URI(fixedUri);
+ }
+
+ // test files exists
+ {
+ Path resourcePath = Paths.get(resourceUri);
+ boolean exists = Files.exists(resourcePath);
+ System.out.println(
+ "Files.exists(resourcePath): " + exists
+ + "\n resourcePath: " + resourcePath
+ + "\n resourceUri: " + resourceUri
+ );
+ }
+ }
+
+ private static void printClassLoaderTree(ClassLoader loader) {
+ StringBuilder message = new StringBuilder();
+
+ message.append("Using current thread context ClassLoader:\n");
+ message.append(" \u25b8");
+ message.append(loader.getClass().getCanonicalName());
+ message.append('\n');
+
+ ClassLoader curClassLoader = loader;
+ int lvl = 0;
+ while ((curClassLoader = curClassLoader.getParent()) != null) {
+ int curLvl = ++lvl;
+ message.append(" ");
+ while (curLvl-- > 1) {
+ message.append(" ");
+ }
+ message.append(" \u2514\u2500\u2574");
+ message.append(curClassLoader.getClass().getCanonicalName());
+ message.append('\n');
+ }
+ System.out.print(message);
+ }
+}
diff --git a/gh-8822/src/main/resources/test.txt b/gh-8822/src/main/resources/test.txt
new file mode 100644
index 0000000..e69de29