Skip to content

Commit

Permalink
Unified API for mapping static servlets #125
Browse files Browse the repository at this point in the history
rethinking static servlet API to shorten the builder
  • Loading branch information
andrus committed May 25, 2024
1 parent 38d8aad commit 2fb2cbb
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ public void configure(Binder binder) {

// tag::pathInfo[]
MappedServlet<?> s = MappedServlet
.ofStatic("docroot")
.urlPatterns("/abc/*")
.ofStatic("/abc/*")
.pathInfoOnly()
.build();
// end::pathInfo[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,19 +126,19 @@ public <T extends EventListener> 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());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,23 @@

import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
* A wrapper around a servlet object that provides access to its URL mapping and parameters.
*/
public class MappedServlet<T extends Servlet> extends MappedWebArtifact<T> {

/**
* 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<String> normalized = urlPatterns != null && urlPatterns.length > 0 ? Set.of(urlPatterns) : Set.of("/");
return new StaticMappedServletBuilder(normalized);
}

public MappedServlet(T servlet, Set<String> urlPatterns) {
Expand Down Expand Up @@ -84,23 +76,19 @@ public T getServlet() {
*/
public static class StaticMappedServletBuilder {

private final String name;
private String[] urlPatterns;
private final Set<String> 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<String> 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;
}

Expand Down Expand Up @@ -129,13 +117,7 @@ public StaticMappedServletBuilder pathInfoOnly() {
}

public MappedServlet<?> build() {

MultiBaseStaticServlet servlet = new MultiBaseStaticServlet(resourceBase, pathInfoOnly);
Set<String> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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/");
Expand Down Expand Up @@ -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");
Expand All @@ -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
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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)

Expand Down

0 comments on commit 2fb2cbb

Please sign in to comment.