Skip to content

Commit 9342317

Browse files
committed
Avoid use of java.net.URL constructors (for JDK 20 compatibility)
Explicit path computation also leads to consistent relative path semantics for resource URLs. Closes gh-29481 Closes gh-28522
1 parent dfd7449 commit 9342317

File tree

4 files changed

+41
-44
lines changed

4 files changed

+41
-44
lines changed

spring-core/src/main/java/org/springframework/core/io/UrlResource.java

+15-17
Original file line numberDiff line numberDiff line change
@@ -89,33 +89,31 @@ public UrlResource(URI uri) throws MalformedURLException {
8989
}
9090

9191
/**
92-
* Create a new {@code UrlResource} based on a URL path.
92+
* Create a new {@code UrlResource} based on a URI path.
9393
* <p>Note: The given path needs to be pre-encoded if necessary.
94-
* @param path a URL path
95-
* @throws MalformedURLException if the given URL path is not valid
96-
* @see java.net.URL#URL(String)
94+
* @param path a URI path
95+
* @throws MalformedURLException if the given URI path is not valid
96+
* @see ResourceUtils#toURI(String)
9797
*/
9898
public UrlResource(String path) throws MalformedURLException {
9999
Assert.notNull(path, "Path must not be null");
100+
String cleanedPath = StringUtils.cleanPath(path);
101+
URI uri;
102+
URL url;
100103

101-
// Equivalent without java.net.URL constructor - for building on JDK 20+
102-
/*
103104
try {
104-
String cleanedPath = StringUtils.cleanPath(path);
105-
this.uri = ResourceUtils.toURI(cleanedPath);
106-
this.url = this.uri.toURL();
107-
this.cleanedUrl = cleanedPath;
105+
// Prefer URI construction with toURL conversion (as of 6.1)
106+
uri = ResourceUtils.toURI(cleanedPath);
107+
url = uri.toURL();
108108
}
109109
catch (URISyntaxException | IllegalArgumentException ex) {
110-
MalformedURLException exToThrow = new MalformedURLException(ex.getMessage());
111-
exToThrow.initCause(ex);
112-
throw exToThrow;
110+
uri = null;
111+
url = ResourceUtils.toURL(path);
113112
}
114-
*/
115113

116-
this.uri = null;
117-
this.url = ResourceUtils.toURL(path);
118-
this.cleanedUrl = StringUtils.cleanPath(path);
114+
this.uri = uri;
115+
this.url = url;
116+
this.cleanedUrl = cleanedPath;
119117
}
120118

121119
/**

spring-core/src/main/java/org/springframework/util/ResourceUtils.java

+5-13
Original file line numberDiff line numberDiff line change
@@ -390,20 +390,17 @@ public static URI toURI(String location) throws URISyntaxException {
390390
* @throws MalformedURLException if the location wasn't a valid URL
391391
* @since 6.0
392392
*/
393+
@SuppressWarnings("deprecation") // on JDK 20
393394
public static URL toURL(String location) throws MalformedURLException {
394-
// Equivalent without java.net.URL constructor - for building on JDK 20+
395-
/*
396395
try {
396+
// Prefer URI construction with toURL conversion (as of 6.1)
397397
return toURI(StringUtils.cleanPath(location)).toURL();
398398
}
399399
catch (URISyntaxException | IllegalArgumentException ex) {
400-
MalformedURLException exToThrow = new MalformedURLException(ex.getMessage());
401-
exToThrow.initCause(ex);
402-
throw exToThrow;
400+
// Lenient fallback to deprecated (on JDK 20) URL constructor,
401+
// e.g. for decoded location Strings with percent characters.
402+
return new URL(location);
403403
}
404-
*/
405-
406-
return new URL(location);
407404
}
408405

409406
/**
@@ -419,12 +416,7 @@ public static URL toRelativeURL(URL root, String relativePath) throws MalformedU
419416
// # can appear in filenames, java.net.URL should not treat it as a fragment
420417
relativePath = StringUtils.replace(relativePath, "#", "%23");
421418

422-
// Equivalent without java.net.URL constructor - for building on JDK 20+
423-
/*
424419
return toURL(StringUtils.applyRelativePath(root.toString(), relativePath));
425-
*/
426-
427-
return new URL(root, relativePath);
428420
}
429421

430422
/**

spring-webflux/src/test/java/org/springframework/web/reactive/resource/PathResourceResolverTests.java

+10-7
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ public class PathResourceResolverTests {
4040

4141
private static final Duration TIMEOUT = Duration.ofSeconds(5);
4242

43-
4443
private final PathResourceResolver resolver = new PathResourceResolver();
4544

4645

@@ -64,7 +63,7 @@ public void resolveFromClasspathRoot() {
6463
assertThat(actual).isNotNull();
6564
}
6665

67-
@Test // gh-22272
66+
@Test // gh-22272
6867
public void resolveWithEncodedPath() throws IOException {
6968
Resource classpathLocation = new ClassPathResource("test/", PathResourceResolver.class);
7069
testWithEncodedPath(classpathLocation);
@@ -108,10 +107,14 @@ private void testCheckResource(Resource location, String requestPath) throws IOE
108107
assertThat(actual).isNull();
109108
}
110109

111-
@Test // gh-23463
110+
@Test // gh-23463
112111
public void ignoreInvalidEscapeSequence() throws IOException {
113112
UrlResource location = new UrlResource(getClass().getResource("./test/"));
114-
Resource resource = location.createRelative("test%file.txt");
113+
114+
Resource resource = new UrlResource(location.getURL() + "test%file.txt");
115+
assertThat(this.resolver.checkResource(resource, location)).isTrue();
116+
117+
resource = location.createRelative("test%file.txt");
115118
assertThat(this.resolver.checkResource(resource, location)).isTrue();
116119
}
117120

@@ -129,7 +132,7 @@ public void checkResourceWithAllowedLocations() {
129132
assertThat(actual).isEqualTo("../testalternatepath/bar.css");
130133
}
131134

132-
@Test // SPR-12624
135+
@Test // SPR-12624
133136
public void checkRelativeLocation() throws Exception {
134137
String location= new UrlResource(getClass().getResource("./test/")).getURL().toExternalForm();
135138
location = location.replace("/test/org/springframework","/test/org/../org/springframework");
@@ -140,13 +143,13 @@ public void checkRelativeLocation() throws Exception {
140143
assertThat(resourceMono.block(TIMEOUT)).isNotNull();
141144
}
142145

143-
@Test // SPR-12747
146+
@Test // SPR-12747
144147
public void checkFileLocation() throws Exception {
145148
Resource resource = getResource("main.css");
146149
assertThat(this.resolver.checkResource(resource, resource)).isTrue();
147150
}
148151

149-
@Test // SPR-13241
152+
@Test // SPR-13241
150153
public void resolvePathRootResource() {
151154
Resource webjarsLocation = new ClassPathResource("/META-INF/resources/webjars/", PathResourceResolver.class);
152155
String path = this.resolver.resolveUrlPathInternal(

spring-webmvc/src/test/java/org/springframework/web/servlet/resource/PathResourceResolverTests.java

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -92,10 +92,14 @@ private void testCheckResource(Resource location, String requestPath) throws IOE
9292
assertThat(actual).isNull();
9393
}
9494

95-
@Test // gh-23463
95+
@Test // gh-23463
9696
public void ignoreInvalidEscapeSequence() throws IOException {
9797
UrlResource location = new UrlResource(getClass().getResource("./test/"));
98-
Resource resource = location.createRelative("test%file.txt");
98+
99+
Resource resource = new UrlResource(location.getURL() + "test%file.txt");
100+
assertThat(this.resolver.checkResource(resource, location)).isTrue();
101+
102+
resource = location.createRelative("test%file.txt");
99103
assertThat(this.resolver.checkResource(resource, location)).isTrue();
100104
}
101105

@@ -112,7 +116,7 @@ public void checkResourceWithAllowedLocations() {
112116
assertThat(actual).isEqualTo("../testalternatepath/bar.css");
113117
}
114118

115-
@Test // SPR-12432
119+
@Test // SPR-12432
116120
public void checkServletContextResource() throws Exception {
117121
Resource classpathLocation = new ClassPathResource("test/", PathResourceResolver.class);
118122
MockServletContext context = new MockServletContext();
@@ -124,7 +128,7 @@ public void checkServletContextResource() throws Exception {
124128
assertThat(this.resolver.checkResource(resource, servletContextLocation)).isTrue();
125129
}
126130

127-
@Test // SPR-12624
131+
@Test // SPR-12624
128132
public void checkRelativeLocation() throws Exception {
129133
String location= new UrlResource(getClass().getResource("./test/")).getURL().toExternalForm();
130134
location = location.replace("/test/org/springframework","/test/org/../org/springframework");
@@ -135,13 +139,13 @@ public void checkRelativeLocation() throws Exception {
135139
assertThat(actual).isNotNull();
136140
}
137141

138-
@Test // SPR-12747
142+
@Test // SPR-12747
139143
public void checkFileLocation() throws Exception {
140144
Resource resource = getResource("main.css");
141145
assertThat(this.resolver.checkResource(resource, resource)).isTrue();
142146
}
143147

144-
@Test // SPR-13241
148+
@Test // SPR-13241
145149
public void resolvePathRootResource() {
146150
Resource webjarsLocation = new ClassPathResource("/META-INF/resources/webjars/", PathResourceResolver.class);
147151
String path = this.resolver.resolveUrlPathInternal("", Collections.singletonList(webjarsLocation), null);

0 commit comments

Comments
 (0)