From e249d394271c3a303bf7fb2cb8415499471041f0 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 12 Aug 2022 10:36:23 +1000 Subject: [PATCH] MemoryResource (#8451) Added MemoryResource to simplify favicon and similar handling --- .../java/org/eclipse/jetty/server/Server.java | 4 +- .../server/handler/DefaultHandlerTest.java | 5 + .../jetty/util/resource/MemoryResource.java | 113 ++++++++++++++++++ .../eclipse/jetty/util/resource/Resource.java | 2 + .../jetty/util/resource/ResourceFactory.java | 13 +- .../util/resource/MemoryResourceTest.java | 34 ++++++ 6 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MemoryResource.java create mode 100644 jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/MemoryResourceTest.java diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 5d8d83eeaf5..fa84178d25e 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -671,12 +671,10 @@ public class Server extends Handler.Wrapper implements Attributes */ private Resource newResource(String name) { - // TODO replace this. It is needlessly complex and inefficient as it holds a mount of the server jar - // just for things like favicon and default stylesheet URL url = getClass().getResource(name); if (url == null) throw new IllegalStateException("Missing server resource: " + name); - return ResourceFactory.of(this).newResource(URI.create(url.toExternalForm())); + return ResourceFactory.root().newMemoryResource(url); } @Override diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DefaultHandlerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DefaultHandlerTest.java index 879fa1b616e..0ecb8ac30c0 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DefaultHandlerTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/handler/DefaultHandlerTest.java @@ -29,7 +29,9 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertEquals; public class DefaultHandlerTest @@ -134,6 +136,9 @@ public class DefaultHandlerTest assertEquals(HttpStatus.OK_200, response.getStatus()); assertEquals("image/x-icon", response.get(HttpHeader.CONTENT_TYPE)); + byte[] bytes = response.getContentBytes(); + assertThat(bytes, notNullValue()); + assertThat(bytes.length, greaterThan(0)); } } } diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MemoryResource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MemoryResource.java new file mode 100644 index 00000000000..b134fdf5ce6 --- /dev/null +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/MemoryResource.java @@ -0,0 +1,113 @@ +// +// ======================================================================== +// 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.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.Path; +import java.util.Objects; + +import org.eclipse.jetty.util.IO; + +/** + *

An in memory Resource created from a {@link URL}

+ */ +public class MemoryResource extends Resource +{ + private final URI _uri; + private final long _created = System.currentTimeMillis(); + private final byte[] _bytes; + + MemoryResource(URL url) + { + try + { + _uri = Objects.requireNonNull(url).toURI(); + try (InputStream in = url.openStream()) + { + _bytes = IO.readBytes(in); + } + } + catch (IOException | URISyntaxException e) + { + throw new RuntimeException(e); + } + } + + @Override + public Path getPath() + { + return Path.of(_uri); + } + + @Override + public boolean isContainedIn(Resource r) + { + return getPath().startsWith(r.getPath()); + } + + @Override + public URI getURI() + { + return _uri; + } + + @Override + public String getName() + { + return getPath().toAbsolutePath().toString(); + } + + @Override + public long lastModified() + { + return _created; + } + + @Override + public long length() + { + return _bytes.length; + } + + @Override + public InputStream newInputStream() throws IOException + { + return new ByteArrayInputStream(_bytes); + } + + @Override + public ReadableByteChannel newReadableByteChannel() throws IOException + { + return Channels.newChannel(newInputStream()); + } + + @Override + public boolean exists() + { + return true; + } + + @Override + public String toString() + { + return getName(); + } +} diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java index 7667cad2c87..26d8e3ed029 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java @@ -275,6 +275,8 @@ public abstract class Resource */ public List list() // TODO: should return Path's { + if (!isDirectory()) + return null; try (DirectoryStream dir = Files.newDirectoryStream(getPath())) { List entries = new ArrayList<>(); diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactory.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactory.java index e4bc1434556..0a744befa74 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactory.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/ResourceFactory.java @@ -104,8 +104,6 @@ public interface ResourceFactory * Find a classpath resource. * 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. - * If it is still not found, then {@link ClassLoader#getSystemResource(String)} is used. - * Unlike {@link ClassLoader#getSystemResource(String)} this method does not check for normal resources. * * @param resource the relative name of the resource * @return Resource or null @@ -129,6 +127,17 @@ public interface ResourceFactory } } + /** + * Load a URL into a memory resource. + * @param url the URL to load into memory + * @return Resource or null + * @see #newClassPathResource(String) + */ + default Resource newMemoryResource(URL url) + { + return new MemoryResource(url); + } + /** * Construct a resource from a string. * diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/MemoryResourceTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/MemoryResourceTest.java new file mode 100644 index 00000000000..dfe995cd28a --- /dev/null +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/MemoryResourceTest.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// 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 org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.Loader; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MemoryResourceTest +{ + @Test + public void testJettyLogging() throws Exception + { + Resource resource = ResourceFactory.root().newMemoryResource(Loader.getResource("jetty-logging.properties")); + assertTrue(resource.exists()); + String contents = IO.toString(resource.newInputStream()); + assertThat(contents, startsWith("#org.eclipse.jetty.util.LEVEL=DEBUG")); + } +}