From 048be1813e0744b01acad15f3e31bcdacbf9ec75 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 12 Jun 2019 14:22:17 +0100 Subject: [PATCH] Ensure JarFile created for nested entry InputStream is closed Closes gh-17127 --- .../boot/loader/jar/JarURLConnection.java | 56 +++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java index 323b4416d87..cca8f63de4c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarURLConnection.java @@ -19,6 +19,7 @@ package org.springframework.boot.loader.jar; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.FilePermission; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -80,14 +81,18 @@ final class JarURLConnection extends java.net.JarURLConnection { private final JarEntryName jarEntryName; + private final CloseAction closeAction; + private JarEntry jarEntry; - private JarURLConnection(URL url, JarFile jarFile, JarEntryName jarEntryName) throws IOException { + private JarURLConnection(URL url, JarFile jarFile, JarEntryName jarEntryName, CloseAction closeAction) + throws IOException { // What we pass to super is ultimately ignored super(EMPTY_JAR_URL); this.url = url; this.jarFile = jarFile; this.jarEntryName = jarEntryName; + this.closeAction = closeAction; } @Override @@ -168,7 +173,17 @@ final class JarURLConnection extends java.net.JarURLConnection { if (inputStream == null) { throwFileNotFound(this.jarEntryName, this.jarFile); } - return inputStream; + return new FilterInputStream(inputStream) { + + @Override + public void close() throws IOException { + super.close(); + if (JarURLConnection.this.closeAction != null) { + JarURLConnection.this.closeAction.perform(); + } + } + + }; } private void throwFileNotFound(Object entry, JarFile jarFile) throws FileNotFoundException { @@ -249,24 +264,30 @@ final class JarURLConnection extends java.net.JarURLConnection { int index = indexOfRootSpec(spec, jarFile.getPathFromRoot()); if (index == -1) { return (Boolean.TRUE.equals(useFastExceptions.get()) ? NOT_FOUND_CONNECTION - : new JarURLConnection(url, null, EMPTY_JAR_ENTRY_NAME)); + : new JarURLConnection(url, null, EMPTY_JAR_ENTRY_NAME, null)); } int separator; + JarFile connectionJarFile = jarFile; while ((separator = spec.indexOf(SEPARATOR, index)) > 0) { JarEntryName entryName = JarEntryName.get(spec.subSequence(index, separator)); JarEntry jarEntry = jarFile.getJarEntry(entryName.toCharSequence()); if (jarEntry == null) { - return JarURLConnection.notFound(jarFile, entryName); + return JarURLConnection.notFound(connectionJarFile, entryName, + (connectionJarFile != jarFile) ? connectionJarFile::close : null); } - jarFile = jarFile.getNestedJarFile(jarEntry); + connectionJarFile = connectionJarFile.getNestedJarFile(jarEntry); index = separator + SEPARATOR.length(); } JarEntryName jarEntryName = JarEntryName.get(spec, index); if (Boolean.TRUE.equals(useFastExceptions.get()) && !jarEntryName.isEmpty() - && !jarFile.containsEntry(jarEntryName.toString())) { + && !connectionJarFile.containsEntry(jarEntryName.toString())) { + if (connectionJarFile != jarFile) { + connectionJarFile.close(); + } return NOT_FOUND_CONNECTION; } - return new JarURLConnection(url, jarFile, jarEntryName); + return new JarURLConnection(url, connectionJarFile, jarEntryName, + (connectionJarFile != jarFile) ? connectionJarFile::close : null); } private static int indexOfRootSpec(StringSequence file, String pathFromRoot) { @@ -279,18 +300,22 @@ final class JarURLConnection extends java.net.JarURLConnection { private static JarURLConnection notFound() { try { - return notFound(null, null); + return notFound(null, null, null); } catch (IOException ex) { throw new IllegalStateException(ex); } } - private static JarURLConnection notFound(JarFile jarFile, JarEntryName jarEntryName) throws IOException { + private static JarURLConnection notFound(JarFile jarFile, JarEntryName jarEntryName, CloseAction closeAction) + throws IOException { if (Boolean.TRUE.equals(useFastExceptions.get())) { + if (closeAction != null) { + closeAction.perform(); + } return NOT_FOUND_CONNECTION; } - return new JarURLConnection(null, jarFile, jarEntryName); + return new JarURLConnection(null, jarFile, jarEntryName, closeAction); } /** @@ -393,4 +418,15 @@ final class JarURLConnection extends java.net.JarURLConnection { } + /** + * An action to be taken when the connection is being "closed" and its underlying + * resources are no longer needed. + */ + @FunctionalInterface + private interface CloseAction { + + void perform() throws IOException; + + } + }