Affects: 5.3.19 (most likely any version)
We were using the combination of ResourcePatternResolver#getResources
and Resource#createRelative
for something and received a bug report from a client that doing Resource#createRelative
was resolving to an entirely different resource.
e.g.
We have an empty folder named reports
on the classpath and if we run the test below it is going to fail because the creation of a relative resource for "http://localhost:8080" will be a resource pointing to that URL and not relative to "classpath*:reports/"
public class UrlResourceTest {
@Test
void testRelativeResource() throws IOException {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
for (Resource resource : resolver.getResources("classpath*:reports/")) {
Resource relative = resource.createRelative("image.png");
assertThat(relative.getURL().toString())
.contains("reports");
Resource relativeUrl = resource.createRelative("http://localhost:8080");
assertThat(relativeUrl.getURL().toString())
.contains("reports");
}
}
}
We realized that in this example the resolved resource for "classpath*:reports/" is a UrlResource
and doing createRelative
on it will delegate to the JDK new URL(this.url, relativePath)
.
We were expecting that doing Resource#createRelative
will treat the passed relative path as relative, irregardless whether it is a URL, or something else. Were we wrong with our expectations?
Comment From: jhoeller
This is indeed caused by the use of java.net.URL
constructors which we intend to get rid of in 6.1 completely: #29481 - since there is heavy use of these facilities all across the Spring ecosystem, we are going to put the changes in place in 6.1 M1 and then go through a milestone phase with them.
For the time being, the best way out is to use StringUtils.applyRelativePath
(or similar) for concatenating the paths manually, then building a Resource
from them.