8344908: URLClassPath should not propagate IllegalArgumentException when finding resources in classpath URLs
Reviewed-by: alanb
This commit is contained in:
parent
ce9d543eb1
commit
81c44e5eb4
@ -903,7 +903,11 @@ public class URLClassPath {
|
||||
private FileLoader(URL url) throws IOException {
|
||||
super(url);
|
||||
String path = url.getFile().replace('/', File.separatorChar);
|
||||
path = ParseUtil.decode(path);
|
||||
try {
|
||||
path = ParseUtil.decode(path);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new IOException(iae);
|
||||
}
|
||||
dir = (new File(path)).getCanonicalFile();
|
||||
@SuppressWarnings("deprecation")
|
||||
var _unused = normalizedBase = new URL(getBaseURL(), ".");
|
||||
|
@ -171,6 +171,7 @@ public final class ParseUtil {
|
||||
* Returns a new String constructed from the specified String by replacing
|
||||
* the URL escape sequences and UTF8 encoding with the characters they
|
||||
* represent.
|
||||
* @throws IllegalArgumentException if {@code s} could not be decoded
|
||||
*/
|
||||
public static String decode(String s) {
|
||||
int n = s.length();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,8 +25,10 @@
|
||||
|
||||
package jdk.internal.loader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.io.File;
|
||||
|
||||
import sun.net.www.ParseUtil;
|
||||
|
||||
/**
|
||||
@ -40,12 +42,12 @@ import sun.net.www.ParseUtil;
|
||||
* @author Michael McMahon
|
||||
*/
|
||||
|
||||
public class FileURLMapper {
|
||||
final class FileURLMapper {
|
||||
|
||||
URL url;
|
||||
String path;
|
||||
private final URL url;
|
||||
private String path;
|
||||
|
||||
public FileURLMapper (URL url) {
|
||||
FileURLMapper(URL url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@ -53,15 +55,18 @@ public class FileURLMapper {
|
||||
* @return the platform specific path corresponding to the URL
|
||||
* so long as the URL does not contain a hostname in the authority field.
|
||||
*/
|
||||
|
||||
public String getPath () {
|
||||
String getPath() throws IOException {
|
||||
if (path != null) {
|
||||
return path;
|
||||
}
|
||||
String host = url.getHost();
|
||||
if (host == null || host.isEmpty() || "localhost".equalsIgnoreCase(host)) {
|
||||
path = url.getFile();
|
||||
path = ParseUtil.decode(path);
|
||||
try {
|
||||
path = ParseUtil.decode(path);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new IOException(iae);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
@ -69,12 +74,12 @@ public class FileURLMapper {
|
||||
/**
|
||||
* Checks whether the file identified by the URL exists.
|
||||
*/
|
||||
public boolean exists () {
|
||||
String s = getPath ();
|
||||
boolean exists() throws IOException {
|
||||
String s = getPath();
|
||||
if (s == null) {
|
||||
return false;
|
||||
} else {
|
||||
File f = new File (s);
|
||||
File f = new File(s);
|
||||
return f.exists();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,8 +25,10 @@
|
||||
|
||||
package jdk.internal.loader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.io.File;
|
||||
|
||||
import sun.net.www.ParseUtil;
|
||||
|
||||
/**
|
||||
@ -36,12 +38,12 @@ import sun.net.www.ParseUtil;
|
||||
* @author Michael McMahon
|
||||
*/
|
||||
|
||||
public class FileURLMapper {
|
||||
final class FileURLMapper {
|
||||
|
||||
URL url;
|
||||
String file;
|
||||
private final URL url;
|
||||
private String file;
|
||||
|
||||
public FileURLMapper (URL url) {
|
||||
FileURLMapper (URL url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@ -49,8 +51,7 @@ public class FileURLMapper {
|
||||
* @return the platform specific path corresponding to the URL, and in particular
|
||||
* returns a UNC when the authority contains a hostname
|
||||
*/
|
||||
|
||||
public String getPath () {
|
||||
String getPath() throws IOException {
|
||||
if (file != null) {
|
||||
return file;
|
||||
}
|
||||
@ -63,13 +64,17 @@ public class FileURLMapper {
|
||||
return file;
|
||||
}
|
||||
String path = url.getFile().replace('/', '\\');
|
||||
file = ParseUtil.decode(path);
|
||||
try {
|
||||
file = ParseUtil.decode(path);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new IOException(iae);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
boolean exists() throws IOException {
|
||||
String path = getPath();
|
||||
File f = new File (path);
|
||||
File f = new File(path);
|
||||
return f.exists();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import jdk.internal.loader.Resource;
|
||||
import jdk.internal.loader.URLClassPath;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assumptions.abort;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8344908
|
||||
* @summary verify that when locating resources, the URLClassPath can function properly
|
||||
* without throwing unexpected exceptions when any URL in the classpath is unusable
|
||||
* @modules java.base/jdk.internal.loader
|
||||
* @run junit ClassPathUnusableURLs
|
||||
*/
|
||||
public class ClassPathUnusableURLs {
|
||||
|
||||
private static final Path SCRATCH_DIR = Path.of(".").normalize();
|
||||
private static final String RESOURCE_NAME = "foo.txt";
|
||||
private static final String SMILEY_EMOJI = "\uD83D\uDE00";
|
||||
|
||||
private static Path ASCII_DIR;
|
||||
private static Path EMOJI_DIR;
|
||||
private static Path JAR_FILE_IN_EMOJI_DIR;
|
||||
private static int NUM_EXPECTED_LOCATED_RESOURCES;
|
||||
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() throws Exception {
|
||||
try {
|
||||
EMOJI_DIR = Files.createTempDirectory(SCRATCH_DIR, SMILEY_EMOJI);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
iae.printStackTrace(); // for debug purpose
|
||||
// if we can't create a directory with an emoji in its path name,
|
||||
// then skip the entire test
|
||||
abort("Skipping test since emoji directory couldn't be created: " + iae);
|
||||
}
|
||||
// successful creation of the dir, continue with the test
|
||||
Files.createFile(EMOJI_DIR.resolve(RESOURCE_NAME));
|
||||
|
||||
ASCII_DIR = Files.createTempDirectory(SCRATCH_DIR, "test-urlclasspath");
|
||||
Files.createFile(ASCII_DIR.resolve(RESOURCE_NAME));
|
||||
|
||||
// create a jar file containing the resource
|
||||
JAR_FILE_IN_EMOJI_DIR = Files.createTempDirectory(SCRATCH_DIR, SMILEY_EMOJI)
|
||||
.resolve("foo.jar");
|
||||
final Manifest manifest = new Manifest();
|
||||
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
|
||||
try (OutputStream fos = Files.newOutputStream(JAR_FILE_IN_EMOJI_DIR);
|
||||
JarOutputStream jos = new JarOutputStream(fos, manifest)) {
|
||||
|
||||
final JarEntry jarEntry = new JarEntry(RESOURCE_NAME);
|
||||
jos.putNextEntry(jarEntry);
|
||||
jos.write("hello".getBytes(US_ASCII));
|
||||
jos.closeEntry();
|
||||
}
|
||||
// Even if the resource is present in more than one classpath element,
|
||||
// we expect it to be found by the URLClassPath only in the path which has just ascii
|
||||
// characters. URLClassPath currently doesn't have the ability to serve resources
|
||||
// from paths containing emoji character(s).
|
||||
NUM_EXPECTED_LOCATED_RESOURCES = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a URLClassPath and then exercises the URLClassPath.findResource()
|
||||
* and URLClassPath.findResources() methods and expects them to return the expected
|
||||
* resources.
|
||||
*/
|
||||
@Test
|
||||
void testFindResource() {
|
||||
// start an empty URL classpath
|
||||
final URLClassPath urlc = new URLClassPath(new URL[0]);
|
||||
final String[] classpathElements = getClassPathElements();
|
||||
try {
|
||||
// use addFile() to construct classpath
|
||||
for (final String path : classpathElements) {
|
||||
urlc.addFile(path);
|
||||
}
|
||||
// findResource()
|
||||
assertNotNull(urlc.findResource(RESOURCE_NAME), "findResource() failed to locate"
|
||||
+ " resource: " + RESOURCE_NAME + " in classpath: "
|
||||
+ Arrays.toString(classpathElements));
|
||||
// findResources()
|
||||
final Enumeration<URL> locatedResources = urlc.findResources(RESOURCE_NAME);
|
||||
assertNotNull(locatedResources, "findResources() failed to"
|
||||
+ " locate resource: " + RESOURCE_NAME + " in classpath: "
|
||||
+ Arrays.toString(classpathElements));
|
||||
int numFound = 0;
|
||||
while (locatedResources.hasMoreElements()) {
|
||||
System.out.println("located " + locatedResources.nextElement()
|
||||
+ " for resource " + RESOURCE_NAME);
|
||||
numFound++;
|
||||
}
|
||||
assertEquals(NUM_EXPECTED_LOCATED_RESOURCES, numFound,
|
||||
"unexpected number of resources located for " + RESOURCE_NAME);
|
||||
} finally {
|
||||
urlc.closeLoaders();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a URLClassPath and then exercises the URLClassPath.getResource()
|
||||
* and URLClassPath.getResources() methods and expects them to return the expected
|
||||
* resources.
|
||||
*/
|
||||
@Test
|
||||
void testGetResource() {
|
||||
// start an empty URL classpath
|
||||
final URLClassPath urlc = new URLClassPath(new URL[0]);
|
||||
final String[] classpathElements = getClassPathElements();
|
||||
try {
|
||||
// use addFile() to construct classpath
|
||||
for (final String path : classpathElements) {
|
||||
urlc.addFile(path);
|
||||
}
|
||||
// getResource()
|
||||
assertNotNull(urlc.getResource(RESOURCE_NAME), "getResource() failed to locate"
|
||||
+ " resource: " + RESOURCE_NAME + " in classpath: "
|
||||
+ Arrays.toString(classpathElements));
|
||||
// getResources()
|
||||
final Enumeration<Resource> locatedResources = urlc.getResources(RESOURCE_NAME);
|
||||
assertNotNull(locatedResources, "getResources() failed to"
|
||||
+ " locate resource: " + RESOURCE_NAME + " in classpath: "
|
||||
+ Arrays.toString(classpathElements));
|
||||
int numFound = 0;
|
||||
while (locatedResources.hasMoreElements()) {
|
||||
System.out.println("located " + locatedResources.nextElement().getURL()
|
||||
+ " for resource " + RESOURCE_NAME);
|
||||
numFound++;
|
||||
}
|
||||
assertEquals(NUM_EXPECTED_LOCATED_RESOURCES, numFound,
|
||||
"unexpected number of resources located for " + RESOURCE_NAME);
|
||||
} finally {
|
||||
urlc.closeLoaders();
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] getClassPathElements() {
|
||||
// Maintain the order - in context of this test, paths with emojis
|
||||
// or those which can't serve the resource should come before the
|
||||
// path that can serve the resource.
|
||||
return new String[]{
|
||||
// non-existent path
|
||||
ASCII_DIR.resolve("non-existent").toString(),
|
||||
// existing emoji dir
|
||||
EMOJI_DIR.toString(),
|
||||
// existing jar file in a emoji dir
|
||||
JAR_FILE_IN_EMOJI_DIR.toString(),
|
||||
// existing ascii dir
|
||||
ASCII_DIR.toString()
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user