Avoid direct URL construction and URL equality checks

Closes gh-29486
This commit is contained in:
Juergen Hoeller 2022-11-14 23:23:12 +01:00
parent 0b21c16fa8
commit aaeb5eb0d2
8 changed files with 87 additions and 59 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,7 +18,6 @@ package org.springframework.beans.factory.xml;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
@ -30,6 +29,7 @@ import org.xml.sax.SAXException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.lang.Nullable;
import org.springframework.util.ResourceUtils;
/**
* {@code EntityResolver} implementation that tries to resolve entity references
@ -82,7 +82,7 @@ public class ResourceEntityResolver extends DelegatingEntityResolver {
String resourcePath = null;
try {
String decodedSystemId = URLDecoder.decode(systemId, StandardCharsets.UTF_8);
String givenUrl = new URL(decodedSystemId).toString();
String givenUrl = ResourceUtils.toURL(decodedSystemId).toString();
String systemRootUrl = new File("").toURI().toURL().toString();
// Try relative to resource base if currently in system root.
if (givenUrl.startsWith(systemRootUrl)) {
@ -116,7 +116,7 @@ public class ResourceEntityResolver extends DelegatingEntityResolver {
url = "https:" + url.substring(5);
}
try {
source = new InputSource(new URL(url).openStream());
source = new InputSource(ResourceUtils.toURL(url).openStream());
source.setPublicId(publicId);
source.setSystemId(systemId);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -77,7 +77,7 @@ public class PathEditor extends PropertyEditorSupport {
boolean nioPathCandidate = !text.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX);
if (nioPathCandidate && !text.startsWith("/")) {
try {
URI uri = new URI(text);
URI uri = ResourceUtils.toURI(text);
if (uri.getScheme() != null) {
nioPathCandidate = false;
// Let's try NIO file system providers via Paths.get(URI)

View File

@ -162,7 +162,7 @@ public class DefaultResourceLoader implements ResourceLoader {
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
URL url = ResourceUtils.toURL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {

View File

@ -31,6 +31,7 @@ import java.nio.charset.StandardCharsets;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
/**
@ -57,10 +58,10 @@ public class UrlResource extends AbstractFileResolvingResource {
private final URL url;
/**
* Cleaned URL (with normalized path), used for comparisons.
* Cleaned URL String (with normalized path), used for comparisons.
*/
@Nullable
private volatile URL cleanedUrl;
private volatile String cleanedUrl;
/**
@ -97,8 +98,8 @@ public class UrlResource extends AbstractFileResolvingResource {
public UrlResource(String path) throws MalformedURLException {
Assert.notNull(path, "Path must not be null");
this.uri = null;
this.url = new URL(path);
this.cleanedUrl = getCleanedUrl(this.url, path);
this.url = ResourceUtils.toURL(path);
this.cleanedUrl = StringUtils.cleanPath(path);
}
/**
@ -144,7 +145,7 @@ public class UrlResource extends AbstractFileResolvingResource {
* Create a new {@code UrlResource} from the given {@link URI}.
* <p>This factory method is a convenience for {@link #UrlResource(URI)} that
* catches any {@link MalformedURLException} and rethrows it wrapped in an
* {@link UncheckedIOException}; suitable for use in {@link java.util.Stream}
* {@link UncheckedIOException}; suitable for use in {@link java.util.stream.Stream}
* and {@link java.util.Optional} APIs or other scenarios when a checked
* {@link IOException} is undesirable.
* @param uri a URI
@ -165,7 +166,7 @@ public class UrlResource extends AbstractFileResolvingResource {
* Create a new {@code UrlResource} from the given URL path.
* <p>This factory method is a convenience for {@link #UrlResource(String)}
* that catches any {@link MalformedURLException} and rethrows it wrapped in an
* {@link UncheckedIOException}; suitable for use in {@link java.util.Stream}
* {@link UncheckedIOException}; suitable for use in {@link java.util.stream.Stream}
* and {@link java.util.Optional} APIs or other scenarios when a checked
* {@link IOException} is undesirable.
* @param path a URL path
@ -183,36 +184,16 @@ public class UrlResource extends AbstractFileResolvingResource {
}
/**
* Determine a cleaned URL for the given original URL.
* @param originalUrl the original URL
* @param originalPath the original URL path
* @return the cleaned URL (possibly the original URL as-is)
* @see org.springframework.util.StringUtils#cleanPath
*/
private static URL getCleanedUrl(URL originalUrl, String originalPath) {
String cleanedPath = StringUtils.cleanPath(originalPath);
if (!cleanedPath.equals(originalPath)) {
try {
return new URL(cleanedPath);
}
catch (MalformedURLException ex) {
// Cleaned URL path cannot be converted to URL -> take original URL.
}
}
return originalUrl;
}
/**
* Lazily determine a cleaned URL for the given original URL.
* @see #getCleanedUrl(URL, String)
*/
private URL getCleanedUrl() {
URL cleanedUrl = this.cleanedUrl;
private String getCleanedUrl() {
String cleanedUrl = this.cleanedUrl;
if (cleanedUrl != null) {
return cleanedUrl;
}
cleanedUrl = getCleanedUrl(this.url, (this.uri != null ? this.uri : this.url).toString());
String originalPath = (this.uri != null ? this.uri : this.url).toString();
cleanedUrl = StringUtils.cleanPath(originalPath);
this.cleanedUrl = cleanedUrl;
return cleanedUrl;
}
@ -305,16 +286,13 @@ public class UrlResource extends AbstractFileResolvingResource {
* A leading slash will get dropped; a "#" symbol will get encoded.
* @since 5.2
* @see #createRelative(String)
* @see java.net.URL#URL(java.net.URL, String)
* @see ResourceUtils#toRelativeURL(URL, String)
*/
protected URL createRelativeURL(String relativePath) throws MalformedURLException {
if (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
}
// # can appear in filenames, java.net.URL should not treat it as a fragment
relativePath = StringUtils.replace(relativePath, "#", "%23");
// Use the URL constructor for applying the relative path as a URL spec
return new URL(this.url, relativePath);
return ResourceUtils.toRelativeURL(this.url, relativePath);
}
/**
@ -324,9 +302,10 @@ public class UrlResource extends AbstractFileResolvingResource {
* @see java.net.URLDecoder#decode(String, java.nio.charset.Charset)
*/
@Override
@Nullable
public String getFilename() {
String filename = StringUtils.getFilename(getCleanedUrl().getPath());
return URLDecoder.decode(filename, StandardCharsets.UTF_8);
String filename = StringUtils.getFilename(this.uri != null ? this.uri.getPath() : this.url.getPath());
return (filename != null ? URLDecoder.decode(filename, StandardCharsets.UTF_8) : null);
}
/**
@ -334,7 +313,7 @@ public class UrlResource extends AbstractFileResolvingResource {
*/
@Override
public String getDescription() {
return "URL [" + this.url + "]";
return "URL [" + (this.uri != null ? this.uri : this.url) + "]";
}

View File

@ -24,6 +24,7 @@ import java.net.URL;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
/**
* JBoss VFS based {@link Resource} implementation.
@ -115,7 +116,7 @@ public class VfsResource extends AbstractResource {
}
}
return new VfsResource(VfsUtils.getRelative(new URL(getURL(), relativePath)));
return new VfsResource(VfsUtils.getRelative(ResourceUtils.toRelativeURL(getURL(), relativePath)));
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -109,7 +109,7 @@ public abstract class ResourceUtils {
return true;
}
try {
new URL(resourceLocation);
toURL(resourceLocation);
return true;
}
catch (MalformedURLException ex) {
@ -141,7 +141,7 @@ public abstract class ResourceUtils {
}
try {
// try URL
return new URL(resourceLocation);
return toURL(resourceLocation);
}
catch (MalformedURLException ex) {
// no URL -> treat as file path
@ -181,7 +181,7 @@ public abstract class ResourceUtils {
}
try {
// try URL
return getFile(new URL(resourceLocation));
return getFile(toURL(resourceLocation));
}
catch (MalformedURLException ex) {
// no URL -> treat as file path
@ -311,7 +311,7 @@ public abstract class ResourceUtils {
if (separatorIndex != -1) {
String jarFile = urlFile.substring(0, separatorIndex);
try {
return new URL(jarFile);
return toURL(jarFile);
}
catch (MalformedURLException ex) {
// Probably no protocol in original jar URL, like "jar:C:/mypath/myjar.jar".
@ -319,7 +319,7 @@ public abstract class ResourceUtils {
if (!jarFile.startsWith("/")) {
jarFile = "/" + jarFile;
}
return new URL(FILE_URL_PREFIX + jarFile);
return toURL(FILE_URL_PREFIX + jarFile);
}
}
else {
@ -346,11 +346,11 @@ public abstract class ResourceUtils {
// Tomcat's "war:file:...mywar.war*/WEB-INF/lib/myjar.jar!/myentry.txt"
String warFile = urlFile.substring(0, endIndex);
if (URL_PROTOCOL_WAR.equals(jarUrl.getProtocol())) {
return new URL(warFile);
return toURL(warFile);
}
int startIndex = warFile.indexOf(WAR_URL_PREFIX);
if (startIndex != -1) {
return new URL(warFile.substring(startIndex + WAR_URL_PREFIX.length()));
return toURL(warFile.substring(startIndex + WAR_URL_PREFIX.length()));
}
}
@ -381,6 +381,53 @@ public abstract class ResourceUtils {
return new URI(StringUtils.replace(location, " ", "%20"));
}
/**
* Create a URL instance for the given location String,
* going through URI construction and then URL conversion.
* @param location the location String to convert into a URL instance
* @return the URL instance
* @throws MalformedURLException if the location wasn't a valid URL
* @since 6.0
*/
public static URL toURL(String location) throws MalformedURLException {
// Not fully equivalent - but to be moved in the given direction
// since JDK 20 deprecates all direct java.net.URL constructors.
/*
try {
return toURI(location).toURL();
}
catch (URISyntaxException ex) {
MalformedURLException exToThrow = new MalformedURLException(ex.getMessage());
exToThrow.initCause(ex);
throw exToThrow;
}
*/
return new URL(location);
}
/**
* Create a URL instance for the given root URL and relative path,
* going through URI construction and then URL conversion.
* @param root the root URL to start from
* @param relativePath the relative path to apply
* @return the relative URL instance
* @throws MalformedURLException if the end result is not a valid URL
* @since 6.0
*/
public static URL toRelativeURL(URL root, String relativePath) throws MalformedURLException {
// # can appear in filenames, java.net.URL should not treat it as a fragment
relativePath = StringUtils.replace(relativePath, "#", "%23");
// Not fully equivalent - but to be moved in the given direction
// since JDK 20 deprecates all direct java.net.URL constructors.
/*
return toURL(StringUtils.applyRelativePath(root.toString(), relativePath));
*/
return new URL(root, relativePath);
}
/**
* Set the {@link URLConnection#setUseCaches "useCaches"} flag on the
* given connection, preferring {@code false} but leaving the

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -312,7 +312,7 @@ final class PersistenceUnitReader {
// relative to the persistence unit root, according to the JPA spec
URL rootUrl = unitInfo.getPersistenceUnitRootUrl();
if (rootUrl != null) {
unitInfo.addJarFileUrl(new URL(rootUrl, value));
unitInfo.addJarFileUrl(ResourceUtils.toRelativeURL(rootUrl, value));
}
else {
logger.warn("Cannot resolve jar-file entry [" + value + "] in persistence unit '" +
@ -363,7 +363,7 @@ final class PersistenceUnitReader {
if (persistenceUnitRoot.endsWith("/")) {
persistenceUnitRoot = persistenceUnitRoot.substring(0, persistenceUnitRoot.length() - 1);
}
return new URL(persistenceUnitRoot);
return ResourceUtils.toURL(persistenceUnitRoot);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -101,6 +101,7 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.StaxUtils;
@ -985,7 +986,7 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
private String getHost(String elementNamespace, DataHandler dataHandler) {
try {
URI uri = new URI(elementNamespace);
URI uri = ResourceUtils.toURI(elementNamespace);
return uri.getHost();
}
catch (URISyntaxException ex) {