From 4b495ca2a99095ef877dab49c642042edcc3432f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 18 Oct 2023 15:00:50 -0700 Subject: [PATCH] Change `NestedLocation` to hold a `Path` rather than a `File` Refactor `NestedLocation` so that it holds a `Path` rather than a `File`. See gh-37668 --- .../net/protocol/jar/UrlJarFileFactory.java | 2 +- .../net/protocol/nested/NestedLocation.java | 15 ++++++++------- .../net/protocol/nested/NestedUrlConnection.java | 16 ++++++++++++---- .../nested/NestedUrlConnectionResources.java | 2 +- .../net/protocol/nested/NestedLocationTests.java | 11 ++++++----- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFileFactory.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFileFactory.java index 1fb8173b87..7a45caea98 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFileFactory.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFileFactory.java @@ -82,7 +82,7 @@ class UrlJarFileFactory { private JarFile createJarFileForNested(URL url, Runtime.Version version, Consumer closeAction) throws IOException { NestedLocation location = NestedLocation.fromUrl(url); - return new UrlNestedJarFile(location.file(), location.nestedEntryName(), version, closeAction); + return new UrlNestedJarFile(location.path().toFile(), location.nestedEntryName(), version, closeAction); } private JarFile createJarFileForStream(URL url, Version version, Consumer closeAction) throws IOException { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedLocation.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedLocation.java index 3f0a016a6a..ff84b3bf84 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedLocation.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedLocation.java @@ -16,8 +16,9 @@ package org.springframework.boot.loader.net.protocol.nested; -import java.io.File; +import java.net.URI; import java.net.URL; +import java.nio.file.Path; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -43,18 +44,18 @@ import org.springframework.boot.loader.net.util.UrlDecoder; * uncompressed entry that contains the nested jar, or a directory entry. The entry must * not start with a {@code '/'}. * - * @param file the zip file that contains the nested entry + * @param path the path to the zip that contains the nested entry * @param nestedEntryName the nested entry name * @author Phillip Webb * @since 3.2.0 */ -public record NestedLocation(File file, String nestedEntryName) { +public record NestedLocation(Path path, String nestedEntryName) { private static final Map cache = new ConcurrentHashMap<>(); public NestedLocation { - if (file == null) { - throw new IllegalArgumentException("'file' must not be null"); + if (path == null) { + throw new IllegalArgumentException("'path' must not be null"); } if (nestedEntryName == null || nestedEntryName.trim().isEmpty()) { throw new IllegalArgumentException("'nestedEntryName' must not be empty"); @@ -86,9 +87,9 @@ public record NestedLocation(File file, String nestedEntryName) { } private static NestedLocation create(int index, String location) { - String file = location.substring(0, index); + String path = location.substring(0, index); String nestedEntryName = location.substring(index + 2); - return new NestedLocation((!file.isEmpty()) ? new File(file) : null, nestedEntryName); + return new NestedLocation((!path.isEmpty()) ? Path.of(path) : null, nestedEntryName); } static void clearCache() { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java index 308b32116d..32051123ab 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnection.java @@ -16,6 +16,7 @@ package org.springframework.boot.loader.net.protocol.nested; +import java.io.File; import java.io.FilePermission; import java.io.FilterInputStream; import java.io.IOException; @@ -25,6 +26,7 @@ import java.lang.ref.Cleaner.Cleanable; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; +import java.nio.file.Files; import java.security.Permission; import org.springframework.boot.loader.ref.Cleaner; @@ -43,7 +45,7 @@ class NestedUrlConnection extends URLConnection { private final Cleanable cleanup; - private long lastModified; + private long lastModified = -1; private FilePermission permission; @@ -91,8 +93,13 @@ class NestedUrlConnection extends URLConnection { @Override public long getLastModified() { - if (this.lastModified == 0) { - this.lastModified = this.resources.getLocation().file().lastModified(); + if (this.lastModified == -1) { + try { + this.lastModified = Files.getLastModifiedTime(this.resources.getLocation().path()).toMillis(); + } + catch (IOException ex) { + this.lastModified = 0; + } } return this.lastModified; } @@ -100,7 +107,8 @@ class NestedUrlConnection extends URLConnection { @Override public Permission getPermission() throws IOException { if (this.permission == null) { - this.permission = new FilePermission(this.resources.getLocation().file().getCanonicalPath(), "read"); + File file = this.resources.getLocation().path().toFile(); + this.permission = new FilePermission(file.getCanonicalPath(), "read"); } return this.permission; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionResources.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionResources.java index 4582e197c4..5806c23928 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionResources.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/nested/NestedUrlConnectionResources.java @@ -51,7 +51,7 @@ class NestedUrlConnectionResources implements Runnable { void connect() throws IOException { synchronized (this) { if (this.zipContent == null) { - this.zipContent = ZipContent.open(this.location.file().toPath(), this.location.nestedEntryName()); + this.zipContent = ZipContent.open(this.location.path(), this.location.nestedEntryName()); try { connectData(); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedLocationTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedLocationTests.java index 0ec9c1f66e..f992624c4f 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedLocationTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/nested/NestedLocationTests.java @@ -18,6 +18,7 @@ package org.springframework.boot.loader.net.protocol.nested; import java.io.File; import java.net.URL; +import java.nio.file.Path; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -44,20 +45,20 @@ class NestedLocationTests { } @Test - void createWhenFileIsNullThrowsException() { + void createWhenPathIsNullThrowsException() { assertThatIllegalArgumentException().isThrownBy(() -> new NestedLocation(null, "nested.jar")) - .withMessageContaining("'file' must not be null"); + .withMessageContaining("'path' must not be null"); } @Test void createWhenNestedEntryNameIsNullThrowsException() { - assertThatIllegalArgumentException().isThrownBy(() -> new NestedLocation(new File("test.jar"), null)) + assertThatIllegalArgumentException().isThrownBy(() -> new NestedLocation(Path.of("test.jar"), null)) .withMessageContaining("'nestedEntryName' must not be empty"); } @Test void createWhenNestedEntryNameIsEmptyThrowsException() { - assertThatIllegalArgumentException().isThrownBy(() -> new NestedLocation(new File("test.jar"), null)) + assertThatIllegalArgumentException().isThrownBy(() -> new NestedLocation(Path.of("test.jar"), null)) .withMessageContaining("'nestedEntryName' must not be empty"); } @@ -91,7 +92,7 @@ class NestedLocationTests { File file = new File(this.temp, "test.jar"); NestedLocation location = NestedLocation .fromUrl(new URL("nested:" + file.getAbsolutePath() + "/!lib/nested.jar")); - assertThat(location.file()).isEqualTo(file); + assertThat(location.path()).isEqualTo(file.toPath()); assertThat(location.nestedEntryName()).isEqualTo("lib/nested.jar"); }