util: Add support for GraalVM Native-Image resource:-URIs and Paths
GraalVM Native-Image makes its classpath resources accessible through an opaque resource: URL scheme. Add support for this scheme in URIUtil and PathResource. Automatically create the NativeImageResourceFileSystem when necessary. Also add a workaround where converting such Native-Image Paths back to URI would yield the wrong URI (potentially a bug in GraalVM). https://github.com/eclipse/jetty.project/issues/9116 https://github.com/oracle/graal/issues/5720 Signed-off-by: Christian Kohlschütter <christian@kohlschutter.com>
This commit is contained in:
parent
1dc175efac
commit
b53e7e063e
|
@ -19,11 +19,14 @@ import java.net.URI;
|
|||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystemNotFoundException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
@ -54,6 +57,7 @@ public final class URIUtil
|
|||
.with("file:")
|
||||
.with("jrt:")
|
||||
.with("jar:")
|
||||
.with("resource:")
|
||||
.build();
|
||||
|
||||
// From https://www.rfc-editor.org/rfc/rfc3986
|
||||
|
@ -211,6 +215,8 @@ public final class URIUtil
|
|||
|
||||
private static final boolean[] ENCODE_PATH_NEEDS_ENCODING;
|
||||
|
||||
private static final String URI_BAD_RESOURCE_PREFIX = "file:///resources!";
|
||||
|
||||
private URIUtil()
|
||||
{
|
||||
}
|
||||
|
@ -1735,6 +1741,54 @@ public final class URIUtil
|
|||
return query1 + '&' + query2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Corrects any bad {@code resource} based URIs, such as those starting with {@code resource:file:///resources!}.
|
||||
*
|
||||
* @param uri
|
||||
* The URI to correct.
|
||||
* @return the corrected URI, or the original URI.
|
||||
* @see <a href="https://github.com/oracle/graal/issues/5720">Graal issue 5720</a>
|
||||
*/
|
||||
public static URI correctResourceURI(URI uri)
|
||||
{
|
||||
if (uri == null || !"resource".equals(uri.getScheme()))
|
||||
return uri;
|
||||
|
||||
String ssp = uri.getSchemeSpecificPart();
|
||||
if (ssp.startsWith(URI_BAD_RESOURCE_PREFIX))
|
||||
{
|
||||
return URI.create("resource:" + ssp.substring(URI_BAD_RESOURCE_PREFIX.length()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper for {@link Paths#get(URI)} and {@link Path#of(URI)}.
|
||||
*
|
||||
* It automatically handles custom file systems, such as ZipFileSystem and NativeImageResourceFileSystem.
|
||||
*
|
||||
* @param uri
|
||||
* The URI.
|
||||
* @return The path.
|
||||
* @throws IOException
|
||||
* on error.
|
||||
*/
|
||||
public static Path getPath(URI uri) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return Path.of(uri);
|
||||
}
|
||||
catch (FileSystemNotFoundException e)
|
||||
{
|
||||
FileSystems.newFileSystem(uri, Collections.emptyMap());
|
||||
return Path.of(uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Corrects any bad {@code file} based URIs (even within a {@code jar:file:} based URIs) from the bad out-of-spec
|
||||
|
|
|
@ -48,6 +48,7 @@ public class PathResource extends Resource
|
|||
.caseSensitive(false)
|
||||
.with("file")
|
||||
.with("jrt")
|
||||
.with("resource")
|
||||
.build();
|
||||
|
||||
// The path object represented by this instance
|
||||
|
@ -141,15 +142,16 @@ public class PathResource extends Resource
|
|||
* Must be an absolute URI using the <code>file</code> scheme.
|
||||
*
|
||||
* @param uri the URI to build this PathResource from.
|
||||
* @throws IOException if the path could not be resolved.
|
||||
*/
|
||||
PathResource(URI uri)
|
||||
PathResource(URI uri) throws IOException
|
||||
{
|
||||
this(uri, false);
|
||||
}
|
||||
|
||||
PathResource(URI uri, boolean bypassAllowedSchemeCheck)
|
||||
PathResource(URI uri, boolean bypassAllowedSchemeCheck) throws IOException
|
||||
{
|
||||
this(Paths.get(uri), uri, bypassAllowedSchemeCheck);
|
||||
this(URIUtil.getPath(URIUtil.correctResourceURI(uri)), uri, bypassAllowedSchemeCheck);
|
||||
}
|
||||
|
||||
PathResource(Path path)
|
||||
|
@ -166,6 +168,7 @@ public class PathResource extends Resource
|
|||
*/
|
||||
PathResource(Path path, URI uri, boolean bypassAllowedSchemeCheck)
|
||||
{
|
||||
uri = URIUtil.correctResourceURI(uri);
|
||||
if (!uri.isAbsolute())
|
||||
throw new IllegalArgumentException("not an absolute uri: " + uri);
|
||||
if (!bypassAllowedSchemeCheck && !SUPPORTED_SCHEMES.contains(uri.getScheme()))
|
||||
|
|
|
@ -13,19 +13,46 @@
|
|||
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
public class PathResourceFactory implements ResourceFactory
|
||||
{
|
||||
@Override
|
||||
public Resource newResource(URI uri)
|
||||
{
|
||||
Path path = Paths.get(uri.normalize());
|
||||
Path path = Path.of(uri.normalize());
|
||||
if (!Files.exists(path))
|
||||
return null;
|
||||
return new PathResource(path, uri, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource newResource(Path path)
|
||||
{
|
||||
if (path == null)
|
||||
return null;
|
||||
|
||||
URI uri = path.toUri();
|
||||
|
||||
try
|
||||
{
|
||||
// Validate URI conversion, and trigger FileSystem initialization, if necessary
|
||||
if (URIUtil.getPath(uri) == null)
|
||||
return null;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Files.exists(path))
|
||||
return null;
|
||||
|
||||
return new PathResource(path, uri, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ class ResourceFactoryInternals
|
|||
PathResourceFactory pathResourceFactory = new PathResourceFactory();
|
||||
RESOURCE_FACTORIES.put("file", pathResourceFactory);
|
||||
RESOURCE_FACTORIES.put("jrt", pathResourceFactory);
|
||||
RESOURCE_FACTORIES.put("resource", pathResourceFactory); // GraalVM native-image resource
|
||||
}
|
||||
|
||||
static ResourceFactory ROOT = new CompositeResourceFactory()
|
||||
|
|
Loading…
Reference in New Issue