Issue #8886 - changes to method names + abstract url impl
This commit is contained in:
parent
ee097316bb
commit
43937b1987
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue