From 2fb2cbb523f352aa664c396d51f8d64c0b61e0e2 Mon Sep 17 00:00:00 2001 From: Andrus Adamchik Date: Sat, 25 May 2024 08:37:35 -0400 Subject: [PATCH] Unified API for mapping static servlets #125 rethinking static servlet API to shorten the builder --- .../_chapters/_02_programming_jetty_apps.adoc | 4 ++ .../io/bootique/jetty/docs/StaticServlet.java | 6 +-- .../jetty/docs/StaticServletPathInfo.java | 3 +- .../bootique/jetty/JettyModuleExtender.java | 8 ++-- .../java/io/bootique/jetty/MappedServlet.java | 42 ++++++------------- .../jetty/MappedServlet_StaticIT.java | 33 +++++++++------ 6 files changed, 44 insertions(+), 52 deletions(-) diff --git a/bootique-jetty-docs/src/main/asciidoc/_chapters/_02_programming_jetty_apps.adoc b/bootique-jetty-docs/src/main/asciidoc/_chapters/_02_programming_jetty_apps.adoc index f198fba..3540e51 100644 --- a/bootique-jetty-docs/src/main/asciidoc/_chapters/_02_programming_jetty_apps.adoc +++ b/bootique-jetty-docs/src/main/asciidoc/_chapters/_02_programming_jetty_apps.adoc @@ -96,6 +96,10 @@ on the application classpath. To "publish" files in a given folder on Jetty, use include::../../../test/java/io/bootique/jetty/docs/StaticServlet.java[tags=staticServlet] ---- +<1> Maps a URL to a "static" servlet +<2> The location of the files served via this servlet +<3> An optional name that would allow to set servlet parameters as shown below + You can publish as many locations as needed. If a location is not known at compile time, it can alternatively be defined in the app configuration with `resourceBase` servlet parameter: diff --git a/bootique-jetty-docs/src/test/java/io/bootique/jetty/docs/StaticServlet.java b/bootique-jetty-docs/src/test/java/io/bootique/jetty/docs/StaticServlet.java index 31967cf..0fccf73 100644 --- a/bootique-jetty-docs/src/test/java/io/bootique/jetty/docs/StaticServlet.java +++ b/bootique-jetty-docs/src/test/java/io/bootique/jetty/docs/StaticServlet.java @@ -30,9 +30,9 @@ public class StaticServlet implements BQModule { @Override public void configure(Binder binder) { MappedServlet s = MappedServlet - .ofStatic("docroot") - .urlPatterns("/") - .resourceBase("classpath:docroot") + .ofStatic("/") // <1> + .resourceBase("classpath:docroot") // <2> + .name("docroot") // <3> .build(); JettyModule.extend(binder).addMappedServlet(s); diff --git a/bootique-jetty-docs/src/test/java/io/bootique/jetty/docs/StaticServletPathInfo.java b/bootique-jetty-docs/src/test/java/io/bootique/jetty/docs/StaticServletPathInfo.java index 5fe9bf1..012d401 100644 --- a/bootique-jetty-docs/src/test/java/io/bootique/jetty/docs/StaticServletPathInfo.java +++ b/bootique-jetty-docs/src/test/java/io/bootique/jetty/docs/StaticServletPathInfo.java @@ -31,8 +31,7 @@ public void configure(Binder binder) { // tag::pathInfo[] MappedServlet s = MappedServlet - .ofStatic("docroot") - .urlPatterns("/abc/*") + .ofStatic("/abc/*") .pathInfoOnly() .build(); // end::pathInfo[] diff --git a/bootique-jetty-jakarta/src/main/java/io/bootique/jetty/JettyModuleExtender.java b/bootique-jetty-jakarta/src/main/java/io/bootique/jetty/JettyModuleExtender.java index 1a96949..04b7d2a 100644 --- a/bootique-jetty-jakarta/src/main/java/io/bootique/jetty/JettyModuleExtender.java +++ b/bootique-jetty-jakarta/src/main/java/io/bootique/jetty/JettyModuleExtender.java @@ -126,19 +126,19 @@ public JettyModuleExtender addMappedListener(TypeLiter } /** - * @deprecated in favor of {@link #addMappedServlet(MappedServlet)} with {@link MappedServlet#ofStatic(String)} + * @deprecated in favor of {@link #addMappedServlet(MappedServlet)} with {@link MappedServlet#ofStatic(String...)} */ @Deprecated(since = "3.0", forRemoval = true) public JettyModuleExtender addStaticServlet(String name, String... urlPatterns) { - return addMappedServlet(MappedServlet.ofStatic(name).urlPatterns(urlPatterns).build()); + return addMappedServlet(MappedServlet.ofStatic(urlPatterns).name(name).build()); } /** - * @deprecated in favor of {@link #addMappedServlet(MappedServlet)} with {@link MappedServlet#ofStatic(String)} + * @deprecated in favor of {@link #addMappedServlet(MappedServlet)} with {@link MappedServlet#ofStatic(String...)} */ @Deprecated(since = "3.0", forRemoval = true) public JettyModuleExtender useDefaultServlet() { - return addMappedServlet(MappedServlet.ofStatic("default").urlPatterns("/").build()); + return addMappedServlet(MappedServlet.ofStatic("/").name("default").build()); } /** diff --git a/bootique-jetty-jakarta/src/main/java/io/bootique/jetty/MappedServlet.java b/bootique-jetty-jakarta/src/main/java/io/bootique/jetty/MappedServlet.java index 6b0a8bf..f090c1f 100644 --- a/bootique-jetty-jakarta/src/main/java/io/bootique/jetty/MappedServlet.java +++ b/bootique-jetty-jakarta/src/main/java/io/bootique/jetty/MappedServlet.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -32,24 +33,15 @@ */ public class MappedServlet extends MappedWebArtifact { - /** - * Starts a builder of a "static" MappedServlet that will act as a web server for a given folder located on - * classpath or the filesystem. - * - * @since 3.0 - */ - public static StaticMappedServletBuilder ofStatic() { - return ofStatic(null); - } - /** * Starts a builder of a named "static" MappedServlet that will act as a web server for a given folder located on - * classpath or the filesystem. + * classpath or the filesystem. If "urlPatterns" parameter is null or empty, the root pattern will be used ("/"). * * @since 3.0 */ - public static StaticMappedServletBuilder ofStatic(String name) { - return new StaticMappedServletBuilder(name); + public static StaticMappedServletBuilder ofStatic(String... urlPatterns) { + Set normalized = urlPatterns != null && urlPatterns.length > 0 ? Set.of(urlPatterns) : Set.of("/"); + return new StaticMappedServletBuilder(normalized); } public MappedServlet(T servlet, Set urlPatterns) { @@ -84,23 +76,19 @@ public T getServlet() { */ public static class StaticMappedServletBuilder { - private final String name; - private String[] urlPatterns; + private final Set urlPatterns; private FolderResourceFactory resourceBase; // capturing this as a String instead of boolean to allow Jetty apply its own string to boolean parsing // later when our value is mixed with the servlet init params private String pathInfoOnly; + private String name; - protected StaticMappedServletBuilder(String name) { - this.name = name; + protected StaticMappedServletBuilder(Set urlPatterns) { + this.urlPatterns = Objects.requireNonNull(urlPatterns); } - /** - * Defines URL patterns for the static servlet. If the call to this method is omitted or the value us null, - * the root pattern will be used ("/"). - */ - public StaticMappedServletBuilder urlPatterns(String... urlPatterns) { - this.urlPatterns = urlPatterns; + public StaticMappedServletBuilder name(String name) { + this.name = name; return this; } @@ -129,13 +117,7 @@ public StaticMappedServletBuilder pathInfoOnly() { } public MappedServlet build() { - - MultiBaseStaticServlet servlet = new MultiBaseStaticServlet(resourceBase, pathInfoOnly); - Set patterns = this.urlPatterns != null && this.urlPatterns.length > 0 - ? Set.of(this.urlPatterns) - : Set.of("/"); - - return new MappedServlet<>(servlet, patterns, name); + return new MappedServlet<>(new MultiBaseStaticServlet(resourceBase, pathInfoOnly), urlPatterns, name); } } } diff --git a/bootique-jetty-jakarta/src/test/java/io/bootique/jetty/MappedServlet_StaticIT.java b/bootique-jetty-jakarta/src/test/java/io/bootique/jetty/MappedServlet_StaticIT.java index d4d730f..f8cbe21 100644 --- a/bootique-jetty-jakarta/src/test/java/io/bootique/jetty/MappedServlet_StaticIT.java +++ b/bootique-jetty-jakarta/src/test/java/io/bootique/jetty/MappedServlet_StaticIT.java @@ -73,7 +73,7 @@ public void commonResourceBase() { testFactory.app("-s") .module(b -> { - MappedServlet s = MappedServlet.ofStatic("sub").urlPatterns("/sub1/*", "/sub2/*").build(); + MappedServlet s = MappedServlet.ofStatic("/sub1/*", "/sub2/*").build(); JettyModule.extend(b).addMappedServlet(s); BQCoreModule.extend(b).setProperty("bq.jetty.staticResourceBase", "src/test/resources/io/bootique/jetty/StaticResourcesIT_docroot_subfolders/"); @@ -104,10 +104,13 @@ public void unnamed() { testFactory.app("-s") .module(b -> JettyModule.extend(b).addMappedServlet(MappedServlet - .ofStatic() - .urlPatterns("/sub1/*") - .resourceBase("classpath:io/bootique/jetty/StaticResourcesIT_docroot_subfolders/") - .build())) + .ofStatic("/sub1/*") + .resourceBase("classpath:io/bootique/jetty/StaticResourcesIT_docroot_subfolders/") + .build()) + .addMappedServlet(MappedServlet + .ofStatic("/sub2/*") + .resourceBase("classpath:io/bootique/jetty/StaticResourcesIT_docroot_subfolders/") + .build())) .run(); WebTarget base = ClientBuilder.newClient().target("http://localhost:8080"); @@ -119,6 +122,10 @@ public void unnamed() { Response r1 = base.path("/sub1/other.txt").request().get(); assertEquals(Response.Status.OK.getStatusCode(), r1.getStatus()); assertEquals("other1", r1.readEntity(String.class)); + + Response r2 = base.path("/sub2/other.txt").request().get(); + assertEquals(Response.Status.OK.getStatusCode(), r2.getStatus()); + assertEquals("other2", r2.readEntity(String.class)); } @Test @@ -192,17 +199,17 @@ public void builderParams() { JettyModule.extend(b) // s1: own resource base and "pathInfoOnly == false" (so servlet path is a part of the // static folder path) - .addMappedServlet(MappedServlet.ofStatic("s1").urlPatterns("/sub1/*").resourceBase("classpath:io/bootique/jetty/ResourcePathResolving/root1/").build()) + .addMappedServlet(MappedServlet.ofStatic("/sub1/*").resourceBase("classpath:io/bootique/jetty/ResourcePathResolving/root1/").build()) // s2: own resource base and "pathInfoOnly == true" (so servlet path is excluded from the // static folder path) - .addMappedServlet(MappedServlet.ofStatic("s2").urlPatterns("/sub2/*").resourceBase("classpath:io/bootique/jetty/ResourcePathResolving/root2/").pathInfoOnly().build()) + .addMappedServlet(MappedServlet.ofStatic("/sub2/*").resourceBase("classpath:io/bootique/jetty/ResourcePathResolving/root2/").pathInfoOnly().build()) // s3: shared resource base and "pathInfoOnly == false" - .addMappedServlet(MappedServlet.ofStatic("s3").urlPatterns("/sub3/*").build()) + .addMappedServlet(MappedServlet.ofStatic("/sub3/*").build()) // s4: shared resource base and "pathInfoOnly == true" - .addMappedServlet(MappedServlet.ofStatic("s4").urlPatterns("/sub4/*").pathInfoOnly().build()); + .addMappedServlet(MappedServlet.ofStatic("/sub4/*").pathInfoOnly().build()); }) .run(); @@ -232,12 +239,12 @@ public void overrideFromParams() { .module(b -> { JettyModule.extend(b) // this "pathInfo" should be overridden by params "pathInfo" of "false" - .addMappedServlet(MappedServlet.ofStatic("s1").urlPatterns("/sub1/*").pathInfoOnly().build()) + .addMappedServlet(MappedServlet.ofStatic("/sub1/*").name("s1").pathInfoOnly().build()) // this resource base should be overridden by params resource base - .addMappedServlet(MappedServlet.ofStatic("s2").urlPatterns("/sub2/*").resourceBase("classpath:io/bootique/jetty/ResourcePathResolving/root1/").build()) - .addMappedServlet(MappedServlet.ofStatic("s3").urlPatterns("/sub3/*").build()) - .addMappedServlet(MappedServlet.ofStatic("s4").urlPatterns("/sub4/*").build()); + .addMappedServlet(MappedServlet.ofStatic("/sub2/*").name("s2").resourceBase("classpath:io/bootique/jetty/ResourcePathResolving/root1/").build()) + .addMappedServlet(MappedServlet.ofStatic("/sub3/*").name("s3").build()) + .addMappedServlet(MappedServlet.ofStatic("/sub4/*").name("s4").build()); BQCoreModule.extend(b)