diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index 88cecb426dfa..792d6f81ffe6 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -19,11 +19,14 @@ 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 static String addQueries(String query1, String query2) 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 Graal issue 5720 + */ + 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); + } + } + /** *
* Corrects any bad {@code file} based URIs (even within a {@code jar:file:} based URIs) from the bad out-of-spec
diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
index 997eb42b4d0b..d2d8ab8f4e4a 100644
--- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
+++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
@@ -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 static boolean isSameName(Path pathA, Path pathB)
* Must be an absolute URI using the file
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 static boolean isSameName(Path pathA, Path pathB)
*/
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()))
diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResourceFactory.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResourceFactory.java
index 4c0737b972f7..4b320a2aac25 100644
--- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResourceFactory.java
+++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResourceFactory.java
@@ -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);
}
}
diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactoryInternals.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactoryInternals.java
index 5f3406e24cd1..2c9ad4462f15 100644
--- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactoryInternals.java
+++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactoryInternals.java
@@ -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()