Issue #8886 - changes to method names + abstract url impl

This commit is contained in:
Joakim Erdfelt 2022-11-14 13:08:50 -06:00
parent ee097316bb
commit 43937b1987
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
4 changed files with 350 additions and 3 deletions

View File

@ -0,0 +1,234 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.util.resource;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Path;
import java.time.Instant;
import org.eclipse.jetty.util.FileID;
/**
* Abstract ResourceFactory for {@link java.net.URL} based resources.
*/
public abstract class AbstractUrlResourceFactory implements ResourceFactory
{
private final String supportedProtocol;
private int connectTimeout;
private boolean useCaches;
protected AbstractUrlResourceFactory(String protocol)
{
this.supportedProtocol = protocol;
this.connectTimeout = Integer.parseInt(System.getProperty(this.getClass().getName() + ".connectTimeout", "1000"));
this.useCaches = Boolean.parseBoolean(System.getProperty(this.getClass().getName() + ".useCaches", "true"));
}
public int getConnectTimeout()
{
return connectTimeout;
}
public void setConnectTimeout(int connectTimeout)
{
this.connectTimeout = connectTimeout;
}
public boolean isUseCaches()
{
return useCaches;
}
public void setUseCaches(boolean useCaches)
{
this.useCaches = useCaches;
}
@Override
public Resource newResource(final URI uri)
{
if (!uri.getScheme().equalsIgnoreCase(supportedProtocol))
throw new IllegalArgumentException("Scheme not support: " + uri.getScheme());
try
{
return new URLResource(uri, this.connectTimeout, this.useCaches);
}
catch (MalformedURLException e)
{
throw new RuntimeException("Bad URI: " + uri, e);
}
}
private static class URLResource extends Resource
{
private final URI uri;
private final URL url;
private final int connectTimeout;
private final boolean useCaches;
public URLResource(URI uri, int connectTimeout, boolean useCaches) throws MalformedURLException
{
this.uri = uri;
this.url = uri.toURL();
this.connectTimeout = connectTimeout;
this.useCaches = useCaches;
}
private URLConnection newConnection() throws IOException
{
URLConnection urlConnection = url.openConnection();
urlConnection.setUseCaches(this.useCaches);
urlConnection.setConnectTimeout(this.connectTimeout);
return urlConnection;
}
@Override
public Path getPath()
{
return null;
}
@Override
public boolean isContainedIn(Resource r)
{
// compare starting URIs?
return false;
}
@Override
public boolean isDirectory()
{
return uri.getPath().endsWith("/");
}
@Override
public boolean isReadable()
{
return exists();
}
@Override
public URI getURI()
{
return uri;
}
@Override
public String getName()
{
return uri.getPath();
}
@Override
public String getFileName()
{
return FileID.getFileName(uri);
}
@Override
public Resource resolve(String subUriPath)
{
URI newURI = uri.resolve(subUriPath);
try
{
return new URLResource(newURI, this.connectTimeout, this.useCaches);
}
catch (MalformedURLException e)
{
return null;
}
}
@Override
public boolean exists()
{
try
{
newConnection();
return true;
}
catch (IOException e)
{
return false;
}
}
@Override
public InputStream newInputStream() throws IOException
{
URLConnection urlConnection = newConnection();
return urlConnection.getInputStream();
}
@Override
public Instant lastModified()
{
try
{
URLConnection urlConnection = newConnection();
return Instant.ofEpochMilli(urlConnection.getLastModified());
}
catch (IOException e)
{
return Instant.EPOCH;
}
}
@Override
public long length()
{
try
{
URLConnection urlConnection = newConnection();
return urlConnection.getContentLengthLong();
}
catch (IOException e)
{
return -1;
}
}
@Override
public ReadableByteChannel newReadableByteChannel()
{
// not really possible with the URL interface
return null;
}
@Override
public boolean isAlias()
{
return false;
}
@Override
public URI getRealURI()
{
return getURI();
}
@Override
public String toString()
{
return String.format("URLResource@%X(%s)", this.uri.hashCode(), this.uri.toASCIIString());
}
}
}

View File

@ -236,11 +236,16 @@ public interface ResourceFactory
return newResource(URIUtil.toJarFileUri(uri)); return newResource(URIUtil.toJarFileUri(uri));
} }
static void addResourceFactory(String scheme, ResourceFactory resource) static void registerResourceFactory(String scheme, ResourceFactory resource)
{ {
ResourceFactoryInternals.RESOURCE_FACTORIES.put(scheme, resource); ResourceFactoryInternals.RESOURCE_FACTORIES.put(scheme, resource);
} }
static ResourceFactory unregisterResourceFactory(String scheme)
{
return ResourceFactoryInternals.RESOURCE_FACTORIES.remove(scheme);
}
static ResourceFactory byScheme(String scheme) static ResourceFactory byScheme(String scheme)
{ {
return ResourceFactoryInternals.RESOURCE_FACTORIES.get(scheme); return ResourceFactoryInternals.RESOURCE_FACTORIES.get(scheme);

View File

@ -19,17 +19,45 @@ import java.nio.file.Path;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ResourceFactoryTest public class ResourceFactoryTest
{ {
@Test @Test
public void testCustomUriScheme() public void testCustomUriSchemeNotRegistered()
{ {
ResourceFactory.addResourceFactory("custom", new CustomResourceFactory()); // Try this as a normal String input first.
// We are subject to the URIUtil.toURI(String) behaviors here.
// Since the `ftp` scheme is not registered, it's not recognized as a supported URI.
// This will be treated as a relative path instead. (and the '//' will be compacted)
Resource resource = ResourceFactory.root().newResource("ftp://webtide.com/favicon.ico");
// Should not find this, as it doesn't exist on the filesystem.
assertNull(resource);
// Now try it as a formal URI object as input.
URI uri = URI.create("ftp://webtide.com/favicon.ico");
// This is an unsupported URI scheme
IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> ResourceFactory.root().newResource(uri));
assertThat(iae.getMessage(), containsString("URI scheme not supported"));
}
@Test
public void testCustomUriSchemeRegistered()
{
ResourceFactory.registerResourceFactory("custom", new CustomResourceFactory());
// Try as a normal String input
Resource resource = ResourceFactory.root().newResource("custom://foo"); Resource resource = ResourceFactory.root().newResource("custom://foo");
assertThat(resource.getURI(), is(URI.create("custom://foo"))); assertThat(resource.getURI(), is(URI.create("custom://foo")));
assertThat(resource.getName(), is("custom-impl")); assertThat(resource.getName(), is("custom-impl"));
// Try as a formal URI object as input
URI uri = URI.create("custom://foo");
resource = ResourceFactory.root().newResource(uri);
assertThat(resource.getURI(), is(URI.create("custom://foo")));
assertThat(resource.getName(), is("custom-impl"));
} }
public static class CustomResourceFactory implements ResourceFactory public static class CustomResourceFactory implements ResourceFactory

View File

@ -0,0 +1,80 @@
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.util.resource;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import org.eclipse.jetty.util.IO;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class UrlResourceFactoryTest
{
@Test
@Tag("external")
public void testHttps() throws IOException
{
ResourceFactory.registerResourceFactory("https", new HttpsResourceFactory());
Resource resource = ResourceFactory.root().newResource(URI.create("https://webtide.com/"));
assertThat(resource, notNullValue());
assertTrue(resource.exists());
try (InputStream in = resource.newInputStream())
{
String result = IO.toString(in, StandardCharsets.UTF_8);
assertThat(result, containsString("webtide.com"));
}
assertThat(resource.lastModified().toEpochMilli(), not(Instant.EPOCH));
assertThat(resource.length(), not(-1));
assertTrue(resource.isDirectory());
assertThat(resource.getFileName(), is(""));
Resource blogs = resource.resolve("blogs/");
assertThat(blogs, notNullValue());
assertTrue(blogs.exists());
assertThat(blogs.lastModified().toEpochMilli(), not(Instant.EPOCH));
assertThat(blogs.length(), not(-1));
assertTrue(blogs.isDirectory());
assertThat(blogs.getFileName(), is(""));
Resource favicon = resource.resolve("favicon.ico");
assertThat(favicon, notNullValue());
assertTrue(favicon.exists());
assertThat(favicon.lastModified().toEpochMilli(), not(Instant.EPOCH));
assertThat(favicon.length(), not(-1));
assertFalse(favicon.isDirectory());
assertThat(favicon.getFileName(), is("favicon.ico"));
}
public static class HttpsResourceFactory extends AbstractUrlResourceFactory
{
public HttpsResourceFactory()
{
super("https");
}
}
}