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..5879a914163 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 @@ -45,6 +45,7 @@ import org.springframework.boot.loader.net.util.UrlDecoder; * @author Phillip Webb * @author Andy Wilkinson * @author Rostyslav Dudka + * @author Sharan Bharmshetty */ final class JarUrlConnection extends java.net.JarURLConnection { @@ -335,15 +336,12 @@ final class JarUrlConnection extends java.net.JarURLConnection { String spec = url.getFile(); if (spec.startsWith("nested:")) { 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"); - } + boolean specHasJarEntry = (separator != -1) && (separator + 2 != spec.length()); + if (specHasJarEntry) { + String jarFileSpec = spec.substring(0, separator); + boolean runtimeRef = "runtime".equals(url.getRef()); + JarFile jarFile = jarFiles.getOrCreateAndCache(jarFileSpec, runtimeRef); 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..6bc0590c019 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 @@ -31,6 +31,7 @@ import java.util.jar.JarFile; * instances. * * @author Phillip Webb + * @author Sharan Bharmshetty */ class UrlJarFiles { @@ -53,6 +54,20 @@ class UrlJarFiles { this.factory = factory; } + JarFile getOrCreateAndCache(String jarFileSpec, boolean runtimeRef) throws IOException { + JarFileUrlKey jarFileUrlKey = new JarFileUrlKey("jar", "", -1, jarFileSpec, runtimeRef); + JarFile jarFile = this.cache.get(jarFileUrlKey); + if (jarFile == null) { + URL jarFileUrl = new URL(jarFileSpec); + if (runtimeRef) { + jarFileUrl = new URL(jarFileUrl, "#runtime"); + } + jarFile = this.factory.createJarFile(jarFileUrl, this::onClose); + this.cache.putIfAbsent(jarFileUrlKey, jarFile, jarFileUrl); + } + return jarFile; + } + /** * Get an existing {@link JarFile} instance from the cache, or create a new * {@link JarFile} instance that can be {@link #cacheIfAbsent(boolean, URL, JarFile) @@ -154,9 +169,17 @@ 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 jarFileUrlKey the jar file URL key + * @return the cached {@link JarFile} or {@code null} + */ + JarFile get(JarFileUrlKey jarFileUrlKey) { synchronized (this) { - return this.jarFileUrlToJarFile.get(urlKey); + return this.jarFileUrlToJarFile.get(jarFileUrlKey); } } @@ -180,11 +203,23 @@ class UrlJarFiles { * they were already there */ boolean putIfAbsent(URL jarFileUrl, JarFile jarFile) { - JarFileUrlKey urlKey = new JarFileUrlKey(jarFileUrl); + return putIfAbsent(new JarFileUrlKey(jarFileUrl), jarFile, jarFileUrl); + } + + /** + * Put the given jar file URL and jar file into the cache if they aren't already + * there. + * @param jarFileUrlKey the jar file URL key + * @param jarFileUrl the jar file URL + * @param jarFile the jar file + * @return {@code true} if the items were added to the cache or {@code false} if + * they were already there + */ + boolean putIfAbsent(JarFileUrlKey jarFileUrlKey, JarFile jarFile, URL jarFileUrl) { synchronized (this) { - JarFile cached = this.jarFileUrlToJarFile.get(urlKey); + JarFile cached = this.jarFileUrlToJarFile.get(jarFileUrlKey); if (cached == null) { - this.jarFileUrlToJarFile.put(urlKey, jarFile); + this.jarFileUrlToJarFile.put(jarFileUrlKey, jarFile); this.jarFileToJarFileUrl.put(jarFile, jarFileUrl); return true; } 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..1628b0ad084 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 @@ -30,6 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link JarFileUrlKey}. * * @author Phillip Webb + * @author Sharan Bharmshetty */ class JarFileUrlKeyTests { @@ -101,4 +102,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); + } + }