diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java index 2b7a05670b2..7d801bcbe44 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java @@ -196,6 +196,30 @@ public class Handler extends URLStreamHandler { setURL(context, JAR_PROTOCOL, null, -1, null, null, file, null, null); } + @Override + protected int hashCode(URL u) { + int result = 0; + String protocol = u.getProtocol(); + if (protocol != null) { + result += protocol.hashCode(); + } + String file = u.getFile(); + int separatorIndex = file.indexOf(SEPARATOR); + if (separatorIndex == -1) { + return result + file.hashCode(); + } + String fileWithoutEntry = file.substring(0, separatorIndex); + try { + result += new URL(fileWithoutEntry).hashCode(); + } + catch (MalformedURLException ex) { + result += fileWithoutEntry.hashCode(); + } + String entry = canonicalize(file.substring(separatorIndex + 2)); + result += entry.hashCode(); + return result; + } + @Override protected boolean sameFile(URL u1, URL u2) { if (!u1.getProtocol().equals("jar") || !u2.getProtocol().equals("jar")) { @@ -209,7 +233,11 @@ public class Handler extends URLStreamHandler { String nested1 = u1.getFile().substring(separator1 + SEPARATOR.length()); String nested2 = u2.getFile().substring(separator2 + SEPARATOR.length()); if (!nested1.equals(nested2)) { - return false; + String canonical1 = canonicalize(nested1); + String canonical2 = canonicalize(nested2); + if (!canonical1.equals(canonical2)) { + return false; + } } String root1 = u1.getFile().substring(0, separator1); String root2 = u2.getFile().substring(0, separator2); @@ -222,6 +250,10 @@ public class Handler extends URLStreamHandler { return super.sameFile(u1, u2); } + private String canonicalize(String path) { + return path.replace(SEPARATOR, "/"); + } + public JarFile getRootJarFileFromUrl(URL url) throws IOException { String spec = url.getFile(); int separatorIndex = spec.indexOf(SEPARATOR); diff --git a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/HandlerTests.java b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/HandlerTests.java index 47862459a35..2fc34b35b10 100644 --- a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/HandlerTests.java +++ b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/HandlerTests.java @@ -119,6 +119,23 @@ public class HandlerTests { new URL("jar:file:/the/path/to/the/first.jar!/content.txt"))).isTrue(); } + @Test + public void sameFileReturnsTrueForUrlsThatReferenceSameFileViaNestedArchiveAndFromRootOfJar() + throws MalformedURLException { + assertThat(this.handler.sameFile( + new URL("jar:file:/test.jar!/BOOT-INF/classes!/foo.txt"), + new URL("jar:file:/test.jar!/BOOT-INF/classes/foo.txt"))).isTrue(); + } + + @Test + public void hashcodesAreEqualForUrlsThatReferenceSameFileViaNestedArchiveAndFromRootOfJar() + throws MalformedURLException { + assertThat(this.handler + .hashCode(new URL("jar:file:/test.jar!/BOOT-INF/classes!/foo.txt"))) + .isEqualTo(this.handler.hashCode( + new URL("jar:file:/test.jar!/BOOT-INF/classes/foo.txt"))); + } + private URL createUrl(String file) throws MalformedURLException { return new URL("jar", null, -1, file, this.handler); }