diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/StaticResourceJars.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/StaticResourceJars.java index 88dd4f98edfd..d23ef2845249 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/StaticResourceJars.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/server/StaticResourceJars.java @@ -115,7 +115,7 @@ private void addUrlConnection(List urls, URL url, URLConnection connection) private boolean isResourcesJar(JarURLConnection connection) { try { - return isResourcesJar(connection.getJarFile()); + return isResourcesJar(connection.getJarFile(), !connection.getUseCaches()); } catch (IOException ex) { return false; @@ -124,16 +124,21 @@ private boolean isResourcesJar(JarURLConnection connection) { private boolean isResourcesJar(File file) { try { - return isResourcesJar(new JarFile(file)); + return isResourcesJar(new JarFile(file), true); } catch (IOException | InvalidPathException ex) { return false; } } - private boolean isResourcesJar(JarFile jar) throws IOException { - try (jar) { - return jar.getName().endsWith(".jar") && (jar.getJarEntry("META-INF/resources") != null); + private boolean isResourcesJar(JarFile jarFile, boolean closeJarFile) throws IOException { + try { + return jarFile.getName().endsWith(".jar") && (jarFile.getJarEntry("META-INF/resources") != null); + } + finally { + if (closeJarFile) { + jarFile.close(); + } } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/StaticResourceJarsTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/StaticResourceJarsTests.java index 47d92ef8f225..074582b9af22 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/StaticResourceJarsTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/StaticResourceJarsTests.java @@ -19,7 +19,10 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.net.JarURLConnection; import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; import java.util.List; import java.util.function.Consumer; import java.util.jar.JarEntry; @@ -29,6 +32,8 @@ import org.junit.jupiter.api.io.TempDir; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatNoException; /** * Tests for {@link StaticResourceJars}. @@ -86,6 +91,27 @@ void ignoreWildcardUrls() throws Exception { assertThat(staticResourceJarUrls).isEmpty(); } + @Test + void doesNotCloseJarFromCachedConnection() throws Exception { + File jarFile = createResourcesJar("test-resources.jar"); + TrackedURLStreamHandler handler = new TrackedURLStreamHandler(true); + URL url = new URL("jar", null, 0, jarFile.toURI().toURL().toString() + "!/", handler); + new StaticResourceJars().getUrlsFrom(url); + assertThatNoException() + .isThrownBy(() -> ((JarURLConnection) handler.getConnection()).getJarFile().getComment()); + } + + @Test + void closesJarFromNonCachedConnection() throws Exception { + File jarFile = createResourcesJar("test-resources.jar"); + TrackedURLStreamHandler handler = new TrackedURLStreamHandler(false); + URL url = new URL("jar", null, 0, jarFile.toURI().toURL().toString() + "!/", handler); + new StaticResourceJars().getUrlsFrom(url); + assertThatIllegalStateException() + .isThrownBy(() -> ((JarURLConnection) handler.getConnection()).getJarFile().getComment()) + .withMessageContaining("closed"); + } + private File createResourcesJar(String name) throws IOException { return createJar(name, (output) -> { JarEntry jarEntry = new JarEntry("META-INF/resources"); @@ -113,4 +139,27 @@ private File createJar(String name, Consumer customizer) throws return jarFile; } + private static class TrackedURLStreamHandler extends URLStreamHandler { + + private final boolean useCaches; + + private URLConnection connection; + + TrackedURLStreamHandler(boolean useCaches) { + this.useCaches = useCaches; + } + + @Override + protected URLConnection openConnection(URL u) throws IOException { + this.connection = new URL(u.toExternalForm()).openConnection(); + this.connection.setUseCaches(this.useCaches); + return this.connection; + } + + URLConnection getConnection() { + return this.connection; + } + + } + }