Issue #10274 - Improve javadoc + move internal methods (#10276)

* Improve javadoc + move internal methods
This commit is contained in:
Joakim Erdfelt 2023-08-16 16:09:36 -05:00 committed by GitHub
parent 24792db09c
commit fd1413db58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 205 additions and 66 deletions

View File

@ -1836,8 +1836,7 @@ public final class URIUtil
Objects.requireNonNull(resource); Objects.requireNonNull(resource);
// Only try URI for string for known schemes, otherwise assume it is a Path // Only try URI for string for known schemes, otherwise assume it is a Path
ResourceFactory resourceFactory = ResourceFactory.getBestByScheme(resource); return (ResourceFactory.isSupported(resource))
return (resourceFactory != null)
? correctFileURI(URI.create(resource)) ? correctFileURI(URI.create(resource))
: Paths.get(resource).toUri(); : Paths.get(resource).toUri();
} }

View File

@ -27,7 +27,89 @@ import org.eclipse.jetty.util.component.Container;
import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.component.Dumpable;
/** /**
* ResourceFactory. * <p>ResourceFactory is the source of new {@link Resource} instances.</p>
*
* <p>
* Some {@link Resource} objects have an internal allocation / release model,
* that the {@link ResourceFactory} is responsible for.
* Once a {@link ResourceFactory} is stopped, the {@link Resource}
* objects created from that {@link ResourceFactory} are released.
* </p>
*
* <h2>A {@link ResourceFactory.LifeCycle} tied to a Jetty {@link org.eclipse.jetty.util.component.Container}</h2>
* <pre>
* ResourceFactory.LifeCycle resourceFactory = ResourceFactory.of(container);
* Resource resource = resourceFactory.newResource(ref);
* </pre>
* <p>
* The use of {@link ResourceFactory#of(Container)} results in a {@link ResourceFactory.LifeCycle} that is tied
* to a specific Jetty {@link org.eclipse.jetty.util.component.Container} such as a {@code Server},
* {@code ServletContextHandler}, or {@code WebAppContext}. This will free the {@code Resource}
* instances created by the {@link org.eclipse.jetty.util.resource.ResourceFactory} once
* the {@code container} that manages it is stopped.
* </p>
*
* <h2>A {@link ResourceFactory.Closeable} that exists within a {@code try-with-resources} call</h2>
* <pre>
* try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) {
* Resource resource = resourceFactory.newResource(ref);
* }
* </pre>
* <p>
* The use of {@link ResourceFactory#closeable()} results in a {@link ResourceFactory} that only exists for
* the duration of the {@code try-with-resources} code block, once this {@code try-with-resources} is closed,
* all {@link Resource} objects associated with that {@link ResourceFactory} are freed as well.
* </p>
*
* <h2>A {@code ResourceFactory} that lives at the JVM level</h2>
* <pre>
* ResourceFactory resourceFactory = ResourceFactory.root();
* Resource resource = resourceFactory.newResource(ref);
* </pre>
* <p>
* The use of {@link ResourceFactory#root()} results in a {@link ResourceFactory} that exists for
* the life of the JVM, and the resources allocated via this {@link ResourceFactory} will not
* be freed until the JVM exits.
* </p>
*
* <h2>Supported URI Schemes</h2>
* <p>
* By default, the following schemes are supported by Jetty.
* </p>
* <dl>
* <dt>file</dt>
* <dd>The standard Java {@code file:/path/to/dir/} syntax</dd>
*
* <dt>jar</dt>
* <dd>The standard Java {@code jar:file:/path/to/file.jar!/} syntax</dd>
*
* <dt>jrt</dt>
* <dd>The standard Java {@code jrt:module-name} syntax</dd>
* </dl>
* <p>
* Special Note: An effort is made to discover any new schemes that
* might be present at JVM startup (eg: graalvm and {@code resource:} scheme).
* At startup Jetty will access an internal Jetty resource (found in
* the jetty-util jar) and seeing what {@code scheme} it is using to access
* it, and will register it with a call to
* {@link ResourceFactory#registerResourceFactory(String, ResourceFactory)}.
* </p>
*
* <h2>Supporting more Schemes</h2>
* <p>
* You can register a new URI scheme to a {@link ResourceFactory} implementation
* using the {@link ResourceFactory#registerResourceFactory(String, ResourceFactory)}
* method, which will cause all new uses of ResourceFactory to use this newly
* registered scheme.
* </p>
* <pre>
* URLResourceFactory urlResourceFactory = new URLResourceFactory();
* urlResourceFactory.setConnectTimeout(1000);
* ResourceFactory.registerResourceFactory("https", urlResourceFactory);
*
* URI web = URI.create("https://eclipse.dev/jetty/");
* Resource resource = ResourceFactory.root().newResource(web);
* </pre>
*/ */
public interface ResourceFactory public interface ResourceFactory
{ {
@ -39,7 +121,6 @@ public interface ResourceFactory
* or null if none are passed. * or null if none are passed.
* The returned {@link Resource} will always return {@code true} from {@link Resource#isDirectory()} * The returned {@link Resource} will always return {@code true} from {@link Resource#isDirectory()}
* @throws IllegalArgumentException if a non-directory resource is passed. * @throws IllegalArgumentException if a non-directory resource is passed.
* @see CombinedResource
*/ */
static Resource combine(List<Resource> resources) static Resource combine(List<Resource> resources)
{ {
@ -54,7 +135,6 @@ public interface ResourceFactory
* or null if none are passed. * or null if none are passed.
* The returned {@link Resource} will always return {@code true} from {@link Resource#isDirectory()} * The returned {@link Resource} will always return {@code true} from {@link Resource#isDirectory()}
* @throws IllegalArgumentException if a non-directory resource is passed. * @throws IllegalArgumentException if a non-directory resource is passed.
* @see CombinedResource
*/ */
static Resource combine(Resource... resources) static Resource combine(Resource... resources)
{ {
@ -65,17 +145,21 @@ public interface ResourceFactory
* Construct a resource from a uri. * Construct a resource from a uri.
* *
* @param uri A URI. * @param uri A URI.
* @return A Resource object. * @return A Resource object, or null if uri points to a location that does not exist.
*/ */
Resource newResource(URI uri); Resource newResource(URI uri);
/** /**
* Construct a system resource from a string. * <p>Construct a system resource from a string.</p>
* The resource is tried as classloader resource before being *
* treated as a normal resource. * <p>
* The resource is first attempted to be accessed via the {@link Thread#getContextClassLoader()}
* before being treated as a normal resource.
* </p>
* *
* @param resource Resource as string representation * @param resource Resource as string representation
* @return The new Resource * @return The new Resource, or null if string points to a location that does not exist
* @throws IllegalArgumentException if string is blank
*/ */
default Resource newSystemResource(String resource) default Resource newSystemResource(String resource)
{ {
@ -134,12 +218,16 @@ public interface ResourceFactory
} }
/** /**
* Find a classpath resource. * <p>Find a classpath resource.</p>
*
* <p>
* The {@link Class#getResource(String)} method is used to lookup the resource. If it is not * The {@link Class#getResource(String)} method is used to lookup the resource. If it is not
* found, then the {@link Loader#getResource(String)} method is used. * found, then the {@link Loader#getResource(String)} method is used.
* </p>
* *
* @param resource the relative name of the resource * @param resource the relative name of the resource
* @return Resource or null * @return Resource, or null if string points to a location that does not exist
* @throws IllegalArgumentException if string is blank
*/ */
default Resource newClassPathResource(String resource) default Resource newClassPathResource(String resource)
{ {
@ -164,9 +252,20 @@ public interface ResourceFactory
} }
/** /**
* <p>
* Load a URL into a memory resource. * Load a URL into a memory resource.
* </p>
*
* <p>
* A Memory Resource is created from a the contents of
* {@link URL#openStream()} and kept in memory from
* that point forward. Never accessing the URL
* again to refresh it's contents.
* </p>
*
* @param url the URL to load into memory * @param url the URL to load into memory
* @return Resource or null * @return Resource, or null if url points to a location that does not exist
* @throws IllegalArgumentException if URL is null
* @see #newClassPathResource(String) * @see #newClassPathResource(String)
*/ */
default Resource newMemoryResource(URL url) default Resource newMemoryResource(URL url)
@ -181,7 +280,9 @@ public interface ResourceFactory
* Construct a resource from a string. * Construct a resource from a string.
* *
* @param resource A URL or filename. * @param resource A URL or filename.
* @return A Resource object. * @return A Resource object, or null if the string points to a location
* that does not exist
* @throws IllegalArgumentException if resource is invalid
*/ */
default Resource newResource(String resource) default Resource newResource(String resource)
{ {
@ -192,10 +293,12 @@ public interface ResourceFactory
} }
/** /**
* Construct a Resource from provided path * Construct a Resource from provided path.
* *
* @param path the path * @param path the path
* @return the Resource for the provided path * @return the Resource for the provided path, or null if the path
* does not exist
* @throws IllegalArgumentException if path is null
*/ */
default Resource newResource(Path path) default Resource newResource(Path path)
{ {
@ -206,10 +309,12 @@ public interface ResourceFactory
} }
/** /**
* Construct a possible {@link CombinedResource} from a list of URIs * Construct a possible combined {@code Resource} from a list of URIs.
* *
* @param uris the URIs * @param uris the URIs
* @return the Resource for the provided path * @return the Resource for the provided URIs, or null if all
* of the provided URIs do not exist
* @throws IllegalArgumentException if list of URIs is empty or null
*/ */
default Resource newResource(List<URI> uris) default Resource newResource(List<URI> uris)
{ {
@ -220,10 +325,12 @@ public interface ResourceFactory
} }
/** /**
* Construct a {@link Resource} from a provided URL * Construct a {@link Resource} from a provided URL.
* *
* @param url the URL * @param url the URL
* @return the Resource for the provided URL * @return the Resource for the provided URL, or null if the
* url points to a location that does not exist
* @throws IllegalArgumentException if url is null
*/ */
default Resource newResource(URL url) default Resource newResource(URL url)
{ {
@ -241,10 +348,12 @@ public interface ResourceFactory
} }
/** /**
* Construct a {@link Resource} from a {@code file:} based URI that is mountable (eg: a jar file) * Construct a {@link Resource} from a {@code file:} based URI that is mountable (eg: a jar file).
* *
* @param uri the URI * @param uri the URI
* @return the Resource, mounted as a {@link java.nio.file.FileSystem} * @return the Resource, mounted as a {@link java.nio.file.FileSystem}, or null if
* the uri points to a location that does not exist.
* @throws IllegalArgumentException if provided URI is not "file" scheme.
*/ */
default Resource newJarFileResource(URI uri) default Resource newJarFileResource(URI uri)
{ {
@ -253,6 +362,28 @@ public interface ResourceFactory
return newResource(URIUtil.toJarFileUri(uri)); return newResource(URIUtil.toJarFileUri(uri));
} }
/**
* Test to see if provided string is supported.
*
* @param str the string to test
* @return true if it is supported
*/
static boolean isSupported(String str)
{
return ResourceFactoryInternals.isSupported(str);
}
/**
* Test to see if provided uri is supported.
*
* @param uri the uri to test
* @return true if it is supported
*/
static boolean isSupported(URI uri)
{
return ResourceFactoryInternals.isSupported(uri);
}
/** /**
* Register a new ResourceFactory that can handle the specific scheme for the Resource API. * Register a new ResourceFactory that can handle the specific scheme for the Resource API.
* *
@ -262,12 +393,14 @@ public interface ResourceFactory
* *
* @param scheme the scheme to support (eg: `ftp`, `http`, `resource`, etc) * @param scheme the scheme to support (eg: `ftp`, `http`, `resource`, etc)
* @param resourceFactory the ResourceFactory to be responsible for the registered scheme. * @param resourceFactory the ResourceFactory to be responsible for the registered scheme.
* @throws IllegalArgumentException if scheme is blank
* @see #unregisterResourceFactory(String) * @see #unregisterResourceFactory(String)
* @see #byScheme(String)
* @see #getBestByScheme(String)
*/ */
static void registerResourceFactory(String scheme, ResourceFactory resourceFactory) static void registerResourceFactory(String scheme, ResourceFactory resourceFactory)
{ {
if (StringUtil.isBlank(scheme))
throw new IllegalArgumentException("Scheme is blank");
ResourceFactoryInternals.RESOURCE_FACTORIES.put(scheme, resourceFactory); ResourceFactoryInternals.RESOURCE_FACTORIES.put(scheme, resourceFactory);
} }
@ -276,53 +409,16 @@ public interface ResourceFactory
* *
* @param scheme the scheme to unregister * @param scheme the scheme to unregister
* @return the existing {@link ResourceFactory} that was registered to that scheme. * @return the existing {@link ResourceFactory} that was registered to that scheme.
* @throws IllegalArgumentException if scheme is blank
* @see #registerResourceFactory(String, ResourceFactory) * @see #registerResourceFactory(String, ResourceFactory)
* @see #byScheme(String)
* @see #getBestByScheme(String)
*/ */
static ResourceFactory unregisterResourceFactory(String scheme) static ResourceFactory unregisterResourceFactory(String scheme)
{ {
if (StringUtil.isBlank(scheme))
throw new IllegalArgumentException("Scheme is blank");
return ResourceFactoryInternals.RESOURCE_FACTORIES.remove(scheme); return ResourceFactoryInternals.RESOURCE_FACTORIES.remove(scheme);
} }
/**
* Get the {@link ResourceFactory} that is registered for the specific scheme.
*
* <pre>{@code
* .byScheme("jar") == ResourceFactory supporting jar
* .byScheme("jar:file://foo.jar!/") == null // full url strings not supported)
* }</pre>
*
* @param scheme the scheme to look up
* @return the {@link ResourceFactory} responsible for the scheme, null if no {@link ResourceFactory} handles the scheme.
* @see #registerResourceFactory(String, ResourceFactory)
* @see #unregisterResourceFactory(String)
* @see #getBestByScheme(String)
*/
static ResourceFactory byScheme(String scheme)
{
return ResourceFactoryInternals.RESOURCE_FACTORIES.get(scheme);
}
/**
* Get the best ResourceFactory for the provided scheme.
*
* <p>
* Unlike {@link #byScheme(String)}, this supports arbitrary Strings, that might start with a supported scheme.
* </p>
*
* @param scheme the scheme to look up
* @return the ResourceFactory that best fits the provided scheme.
* @see org.eclipse.jetty.util.Index#getBest(String)
* @see #registerResourceFactory(String, ResourceFactory)
* @see #unregisterResourceFactory(String)
* @see #byScheme(String)
*/
static ResourceFactory getBestByScheme(String scheme)
{
return ResourceFactoryInternals.RESOURCE_FACTORIES.getBest(scheme);
}
/** /**
* The JVM wide (root) ResourceFactory. * The JVM wide (root) ResourceFactory.
* *

View File

@ -24,6 +24,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.Index;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.component.Dumpable;
@ -93,6 +94,39 @@ class ResourceFactoryInternals
} }
}; };
/**
* Test uri to know if a {@link ResourceFactory} is registered for it.
*
* @param uri the uri to test
* @return true if a ResourceFactory is registered to support the uri
* @see ResourceFactory#registerResourceFactory(String, ResourceFactory)
* @see ResourceFactory#unregisterResourceFactory(String)
* @see #isSupported(String)
*/
static boolean isSupported(URI uri) // TODO: boolean isSupported
{
if (uri == null || uri.getScheme() == null)
return false;
return RESOURCE_FACTORIES.get(uri.getScheme()) != null;
}
/**
* Test string to know if a {@link ResourceFactory} is registered for it.
*
* @param str the string representing the resource location
* @return true if a ResourceFactory is registered to support the string representation
* @see org.eclipse.jetty.util.Index#getBest(String)
* @see ResourceFactory#registerResourceFactory(String, ResourceFactory)
* @see ResourceFactory#unregisterResourceFactory(String)
* @see #isSupported(URI)
*/
static boolean isSupported(String str)
{
if (StringUtil.isBlank(str))
return false;
return RESOURCE_FACTORIES.getBest(str) != null;
}
static class Closeable implements ResourceFactory.Closeable static class Closeable implements ResourceFactory.Closeable
{ {
private final CompositeResourceFactory _compositeResourceFactory = new CompositeResourceFactory(); private final CompositeResourceFactory _compositeResourceFactory = new CompositeResourceFactory();
@ -168,7 +202,7 @@ class ResourceFactoryInternals
uri = URIUtil.correctFileURI(uri); uri = URIUtil.correctFileURI(uri);
} }
ResourceFactory resourceFactory = ResourceFactory.byScheme(uri.getScheme()); ResourceFactory resourceFactory = RESOURCE_FACTORIES.get(uri.getScheme());
if (resourceFactory == null) if (resourceFactory == null)
throw new IllegalArgumentException("URI scheme not supported: " + uri); throw new IllegalArgumentException("URI scheme not supported: " + uri);
if (resourceFactory instanceof MountedPathResourceFactory) if (resourceFactory instanceof MountedPathResourceFactory)

View File

@ -12,7 +12,17 @@
// //
/** /**
* Jetty Util : Common Resource Utilities * <p>Jetty Util : Resource Utilities</p>
*
* <p>
* A {@link org.eclipse.jetty.util.resource.Resource} in Jetty is an abstraction that
* allows for a common API to access various forms of resource sources across the Jetty
* ecosystem.
* </p>
* <p>
* A {@link org.eclipse.jetty.util.resource.Resource} is created via one of the
* {@link org.eclipse.jetty.util.resource.ResourceFactory}{@code .newResource(...)} APIs.
* </p>
*/ */
package org.eclipse.jetty.util.resource; package org.eclipse.jetty.util.resource;