Skip to content

Commit 194fe4b

Browse files
committed
Merge branch '3.5.x'
Closes gh-46403
2 parents decc32d + 7f62b20 commit 194fe4b

File tree

4 files changed

+76
-73
lines changed

4 files changed

+76
-73
lines changed

loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/Handler.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ static int indexOfSeparator(String spec, int start, int limit) {
183183
* Clear any internal caches.
184184
*/
185185
public static void clearCache() {
186-
JarFileUrlKey.clearCache();
187186
JarUrlConnection.clearCache();
188187
}
189188

loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKey.java

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,68 +16,56 @@
1616

1717
package org.springframework.boot.loader.net.protocol.jar;
1818

19-
import java.lang.ref.SoftReference;
2019
import java.net.URL;
21-
import java.util.Locale;
22-
import java.util.Map;
23-
import java.util.concurrent.ConcurrentHashMap;
20+
import java.util.Objects;
2421

2522
/**
26-
* Utility to generate a string key from a jar file {@link URL} that can be used as a
27-
* cache key.
23+
* A fast cache key for a jar file {@link URL} that doesn't trigger DNS lookups.
2824
*
2925
* @author Phillip Webb
3026
*/
3127
final class JarFileUrlKey {
3228

33-
private static volatile SoftReference<Map<URL, String>> cache;
29+
private final String protocol;
3430

35-
private JarFileUrlKey() {
36-
}
31+
private final String host;
3732

38-
/**
39-
* Get the {@link JarFileUrlKey} for the given URL.
40-
* @param url the source URL
41-
* @return a {@link JarFileUrlKey} instance
42-
*/
43-
static String get(URL url) {
44-
if (!isCachableUrl(url)) {
45-
return create(url);
46-
}
47-
Map<URL, String> cache = (JarFileUrlKey.cache != null) ? JarFileUrlKey.cache.get() : null;
48-
if (cache == null) {
49-
cache = new ConcurrentHashMap<>();
50-
JarFileUrlKey.cache = new SoftReference<>(cache);
51-
}
52-
return cache.computeIfAbsent(url, JarFileUrlKey::create);
33+
private final int port;
34+
35+
private final String file;
36+
37+
private final boolean runtimeRef;
38+
39+
JarFileUrlKey(URL url) {
40+
this.protocol = url.getProtocol();
41+
this.host = url.getHost();
42+
this.port = (url.getPort() != -1) ? url.getPort() : url.getDefaultPort();
43+
this.file = url.getFile();
44+
this.runtimeRef = "runtime".equals(url.getRef());
5345
}
5446

55-
private static boolean isCachableUrl(URL url) {
56-
// Don't cache URL that have a host since equals() will perform DNS lookup
57-
return url.getHost() == null || url.getHost().isEmpty();
47+
@Override
48+
public int hashCode() {
49+
return Objects.hashCode(this.file);
5850
}
5951

60-
private static String create(URL url) {
61-
StringBuilder value = new StringBuilder();
62-
String protocol = url.getProtocol();
63-
String host = url.getHost();
64-
int port = (url.getPort() != -1) ? url.getPort() : url.getDefaultPort();
65-
String file = url.getFile();
66-
value.append(protocol.toLowerCase(Locale.ROOT));
67-
value.append(":");
68-
if (host != null && !host.isEmpty()) {
69-
value.append(host.toLowerCase(Locale.ROOT));
70-
value.append((port != -1) ? ":" + port : "");
52+
@Override
53+
public boolean equals(Object obj) {
54+
if (this == obj) {
55+
return true;
7156
}
72-
value.append((file != null) ? file : "");
73-
if ("runtime".equals(url.getRef())) {
74-
value.append("#runtime");
57+
if (obj == null || getClass() != obj.getClass()) {
58+
return false;
7559
}
76-
return value.toString();
60+
JarFileUrlKey other = (JarFileUrlKey) obj;
61+
// We check file first as case sensitive and the most likely item to be different
62+
return Objects.equals(this.file, other.file) && equalsIgnoringCase(this.protocol, other.protocol)
63+
&& equalsIgnoringCase(this.host, other.host) && (this.port == other.port)
64+
&& (this.runtimeRef == other.runtimeRef);
7765
}
7866

79-
static void clearCache() {
80-
cache = null;
67+
private boolean equalsIgnoringCase(String s1, String s2) {
68+
return (s1 == s2) || (s1 != null && s1.equalsIgnoreCase(s2));
8169
}
8270

8371
}

loader/spring-boot-loader/src/main/java/org/springframework/boot/loader/net/protocol/jar/UrlJarFiles.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ void clearCache() {
144144
*/
145145
private static final class Cache {
146146

147-
private final Map<String, JarFile> jarFileUrlToJarFile = new HashMap<>();
147+
private final Map<JarFileUrlKey, JarFile> jarFileUrlToJarFile = new HashMap<>();
148148

149149
private final Map<JarFile, URL> jarFileToJarFileUrl = new HashMap<>();
150150

@@ -154,7 +154,7 @@ private static final class Cache {
154154
* @return the cached {@link JarFile} or {@code null}
155155
*/
156156
JarFile get(URL jarFileUrl) {
157-
String urlKey = JarFileUrlKey.get(jarFileUrl);
157+
JarFileUrlKey urlKey = new JarFileUrlKey(jarFileUrl);
158158
synchronized (this) {
159159
return this.jarFileUrlToJarFile.get(urlKey);
160160
}
@@ -180,7 +180,7 @@ URL get(JarFile jarFile) {
180180
* they were already there
181181
*/
182182
boolean putIfAbsent(URL jarFileUrl, JarFile jarFile) {
183-
String urlKey = JarFileUrlKey.get(jarFileUrl);
183+
JarFileUrlKey urlKey = new JarFileUrlKey(jarFileUrl);
184184
synchronized (this) {
185185
JarFile cached = this.jarFileUrlToJarFile.get(urlKey);
186186
if (cached == null) {
@@ -200,7 +200,7 @@ void remove(JarFile jarFile) {
200200
synchronized (this) {
201201
URL removedUrl = this.jarFileToJarFileUrl.remove(jarFile);
202202
if (removedUrl != null) {
203-
this.jarFileUrlToJarFile.remove(JarFileUrlKey.get(removedUrl));
203+
this.jarFileUrlToJarFile.remove(new JarFileUrlKey(removedUrl));
204204
}
205205
}
206206
}

loader/spring-boot-loader/src/test/java/org/springframework/boot/loader/net/protocol/jar/JarFileUrlKeyTests.java

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.loader.net.protocol.jar;
1818

19+
import java.net.MalformedURLException;
1920
import java.net.URL;
2021

2122
import org.junit.jupiter.api.BeforeAll;
@@ -38,51 +39,66 @@ static void setup() {
3839
}
3940

4041
@Test
41-
void getCreatesKey() throws Exception {
42-
URL url = new URL("jar:nested:/my.jar/!mynested.jar!/my/path");
43-
assertThat(JarFileUrlKey.get(url)).isEqualTo("jar:nested:/my.jar/!mynested.jar!/my/path");
42+
void equalsAndHashCode() throws Exception {
43+
JarFileUrlKey k1 = key("jar:nested:/my.jar/!mynested.jar!/my/path");
44+
JarFileUrlKey k2 = key("jar:nested:/my.jar/!mynested.jar!/my/path");
45+
JarFileUrlKey k3 = key("jar:nested:/my.jar/!mynested.jar!/my/path2");
46+
assertThat(k1.hashCode()).isEqualTo(k2.hashCode())
47+
.isEqualTo("nested:/my.jar/!mynested.jar!/my/path".hashCode());
48+
assertThat(k1).isEqualTo(k1).isEqualTo(k2).isNotEqualTo(k3);
4449
}
4550

4651
@Test
47-
void getWhenUppercaseProtocolCreatesKey() throws Exception {
48-
URL url = new URL("JAR:nested:/my.jar/!mynested.jar!/my/path");
49-
assertThat(JarFileUrlKey.get(url)).isEqualTo("jar:nested:/my.jar/!mynested.jar!/my/path");
52+
void equalsWhenUppercaseAndLowercaseProtocol() throws Exception {
53+
JarFileUrlKey k1 = key("JAR:nested:/my.jar/!mynested.jar!/my/path");
54+
JarFileUrlKey k2 = key("jar:nested:/my.jar/!mynested.jar!/my/path");
55+
assertThat(k1).isEqualTo(k2);
5056
}
5157

5258
@Test
53-
void getWhenHasHostAndPortCreatesKey() throws Exception {
54-
URL url = new URL("https://example.com:1234/test");
55-
assertThat(JarFileUrlKey.get(url)).isEqualTo("https:example.com:1234/test");
59+
void equalsWhenHasHostAndPort() throws Exception {
60+
JarFileUrlKey k1 = key("https://example.com:1234/test");
61+
JarFileUrlKey k2 = key("https://example.com:1234/test");
62+
assertThat(k1).isEqualTo(k2);
5663
}
5764

5865
@Test
59-
void getWhenHasUppercaseHostCreatesKey() throws Exception {
60-
URL url = new URL("https://EXAMPLE.com:1234/test");
61-
assertThat(JarFileUrlKey.get(url)).isEqualTo("https:example.com:1234/test");
66+
void equalsWhenHasUppercaseAndLowercaseHost() throws Exception {
67+
JarFileUrlKey k1 = key("https://EXAMPLE.com:1234/test");
68+
JarFileUrlKey k2 = key("https://example.com:1234/test");
69+
assertThat(k1).isEqualTo(k2);
6270
}
6371

6472
@Test
65-
void getWhenHasNoPortCreatesKeyWithDefaultPort() throws Exception {
66-
URL url = new URL("https://EXAMPLE.com/test");
67-
assertThat(JarFileUrlKey.get(url)).isEqualTo("https:example.com:443/test");
73+
void equalsWhenHasNoPortUsesDefaultPort() throws Exception {
74+
JarFileUrlKey k1 = key("https://EXAMPLE.com/test");
75+
JarFileUrlKey k2 = key("https://example.com:443/test");
76+
assertThat(k1).isEqualTo(k2);
6877
}
6978

7079
@Test
71-
void getWhenHasNoFileCreatesKey() throws Exception {
72-
URL url = new URL("https://EXAMPLE.com");
73-
assertThat(JarFileUrlKey.get(url)).isEqualTo("https:example.com:443");
80+
void equalsWhenHasNoFile() throws Exception {
81+
JarFileUrlKey k1 = key("https://EXAMPLE.com");
82+
JarFileUrlKey k2 = key("https://example.com:443");
83+
assertThat(k1).isEqualTo(k2);
7484
}
7585

7686
@Test
77-
void getWhenHasRuntimeRefCreatesKey() throws Exception {
78-
URL url = new URL("jar:nested:/my.jar/!mynested.jar!/my/path#runtime");
79-
assertThat(JarFileUrlKey.get(url)).isEqualTo("jar:nested:/my.jar/!mynested.jar!/my/path#runtime");
87+
void equalsWhenHasRuntimeRef() throws Exception {
88+
JarFileUrlKey k1 = key("jar:nested:/my.jar/!mynested.jar!/my/path#runtime");
89+
JarFileUrlKey k2 = key("jar:nested:/my.jar/!mynested.jar!/my/path#runtime");
90+
assertThat(k1).isEqualTo(k2);
8091
}
8192

8293
@Test
83-
void getWhenHasOtherRefCreatesKeyWithoutRef() throws Exception {
84-
URL url = new URL("jar:nested:/my.jar/!mynested.jar!/my/path#example");
85-
assertThat(JarFileUrlKey.get(url)).isEqualTo("jar:nested:/my.jar/!mynested.jar!/my/path");
94+
void equalsWhenHasOtherRefIgnoresRefs() throws Exception {
95+
JarFileUrlKey k1 = key("jar:nested:/my.jar/!mynested.jar!/my/path#example");
96+
JarFileUrlKey k2 = key("jar:nested:/my.jar/!mynested.jar!/my/path");
97+
assertThat(k1).isEqualTo(k2);
98+
}
99+
100+
private JarFileUrlKey key(String spec) throws MalformedURLException {
101+
return new JarFileUrlKey(new URL(spec));
86102
}
87103

88104
}

0 commit comments

Comments
 (0)