diff --git a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKey.java b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKey.java index cae978d133c..44df4bc1d9d 100644 --- a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKey.java +++ b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKey.java @@ -44,6 +44,14 @@ final class JarFileUrlKey { this.runtimeRef = "runtime".equals(url.getRef()); } + JarFileUrlKey(String protocol, String host, int port, String file, boolean runtimeRef) { + this.protocol = protocol; + this.host = host; + this.port = port; + this.file = file; + this.runtimeRef = runtimeRef; + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java index ba6521de5de..0b78cc298e1 100644 --- a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java +++ b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarUrlConnection.java @@ -337,13 +337,19 @@ final class JarUrlConnection extends java.net.JarURLConnection { int separator = spec.indexOf("!/"); boolean specHasEntry = (separator != -1) && (separator + 2 != spec.length()); if (specHasEntry) { - URL jarFileUrl = new URL(spec.substring(0, separator)); - if ("runtime".equals(url.getRef())) { - jarFileUrl = new URL(jarFileUrl, "#runtime"); + String jarFileSpec = spec.substring(0, separator); + boolean runtimeRef = "runtime".equals(url.getRef()); + JarFileUrlKey urlKey = new JarFileUrlKey("jar", "", -1, jarFileSpec, runtimeRef); + JarFile jarFile = jarFiles.getCached(urlKey); + if (jarFile == null) { + URL jarFileUrl = new URL(jarFileSpec); + if (runtimeRef) { + jarFileUrl = new URL(jarFileUrl, "#runtime"); + } + jarFile = jarFiles.getOrCreate(true, jarFileUrl); + jarFiles.cacheIfAbsent(true, jarFileUrl, jarFile); } String entryName = UrlDecoder.decode(spec.substring(separator + 2)); - JarFile jarFile = jarFiles.getOrCreate(true, jarFileUrl); - jarFiles.cacheIfAbsent(true, jarFileUrl, jarFile); if (!hasEntry(jarFile, entryName)) { return notFoundConnection(jarFile.getName(), entryName); } diff --git a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFiles.java b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFiles.java index e2dbb3a29e9..c848d62e1ca 100644 --- a/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFiles.java +++ b/loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFiles.java @@ -81,6 +81,15 @@ class UrlJarFiles { return this.cache.get(jarFileUrl); } + /** + * Return the cached {@link JarFile} if available. + * @param urlKey the jar file URL key + * @return the cached jar or {@code null} + */ + JarFile getCached(JarFileUrlKey urlKey) { + return this.cache.get(urlKey); + } + /** * Cache the given {@link JarFile} if caching can be used and there is no existing * entry. @@ -154,7 +163,15 @@ class UrlJarFiles { * @return the cached {@link JarFile} or {@code null} */ JarFile get(URL jarFileUrl) { - JarFileUrlKey urlKey = new JarFileUrlKey(jarFileUrl); + return get(new JarFileUrlKey(jarFileUrl)); + } + + /** + * Get a {@link JarFile} from the cache given a jar file URL key. + * @param urlKey the jar file URL key + * @return the cached {@link JarFile} or {@code null} + */ + JarFile get(JarFileUrlKey urlKey) { synchronized (this) { return this.jarFileUrlToJarFile.get(urlKey); } diff --git a/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java b/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java index 575630819aa..34bb217870b 100644 --- a/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java +++ b/loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java @@ -101,4 +101,27 @@ class JarFileUrlKeyTests { return new JarFileUrlKey(new URL(spec)); } + @Test + void directKeyEqualsUrlKey() throws Exception { + JarFileUrlKey fromUrl = key("jar:nested:/my.jar/!mynested.jar!/my/path"); + JarFileUrlKey direct = new JarFileUrlKey("jar", "", -1, "nested:/my.jar/!mynested.jar!/my/path", false); + assertThat(direct).isEqualTo(fromUrl); + assertThat(direct.hashCode()).isEqualTo(fromUrl.hashCode()); + } + + @Test + void directKeyWithRuntimeRefEqualsUrlKey() throws Exception { + JarFileUrlKey fromUrl = key("jar:nested:/my.jar/!mynested.jar!/my/path#runtime"); + JarFileUrlKey direct = new JarFileUrlKey("jar", "", -1, "nested:/my.jar/!mynested.jar!/my/path", true); + assertThat(direct).isEqualTo(fromUrl); + assertThat(direct.hashCode()).isEqualTo(fromUrl.hashCode()); + } + + @Test + void directKeyWithRuntimeRefNotEqualToKeyWithout() { + JarFileUrlKey k1 = new JarFileUrlKey("jar", "", -1, "nested:/my.jar/!mynested.jar!/my/path", true); + JarFileUrlKey k2 = new JarFileUrlKey("jar", "", -1, "nested:/my.jar/!mynested.jar!/my/path", false); + assertThat(k1).isNotEqualTo(k2); + } + }