Skip to content

Web features

Servlets, filters

Servlets and filters could be registered either with guice ServletModule or using extensions.

Guice servlet module

Example:

public class WebModule extends ServletModule {

    @Override
    protected void configureServlets() {
        filter("/*").through(MyFilter.class);
        serve("/myservlet").with(MyServlet.class);
    }
}  

Pros

Only ServletModule allows mappings by regexp:

serveRegex("(.)*ajax(.)*").with(MyAjaxServlet.class)

Warning

It is important to note that GuiceFilter dispatch all requests for filters and servlets registered by ServletModule internally and so you may have problems combining servlets from ServletModule with filters in main scope.

It is never a blocking issues, but often "not obvious to understand" situations.

Web extensions

Extensions declared with standard jakarta.servlet annotations.

Servlet registration:

@WebServlet("/mapped")
public class MyServlet extends HttpServlet { ... }

Extension recognized by @WebServlet annotation.

Could be registered on admin context:

@WebServlet("/mapped")
@AdminContext
public class MyServlet extends HttpServlet { ... }

Or even on both contexts at the same time: @AdminContext(andAdmin=true).

Filter:

@WebFilter("/some/*")
public class MyFilter implements Filter { ... }

Extension recognized by @WebFilter annotation.

Web listeners (servlet, request, session):

@WebListener
public class MyListener implements ServletContextListener {...}

Extension recognized by @WebListener annotation.

Pros

Installation through extensions has more abilities comparing to ServletModule:

If you don't want to use web installers or have problems with it (e.g. because they use jakarta.servlet annotations) you can disable all of them at once by disabling bundle:

GuiceBundle.builder()
    .disableBindles(WebInstallersBundle.class)
    ...

Manual registration

Alternatively, you can always register servlet or filter manually with dropwizard api:

public class App extends Application {
    public void initialize(Bootstrap bootstrap) {
        bootstrap.addBundle(GuiceBundle.builder().build());
    }

    public void run(Configuration configuration, Environment environment) {
        final MyFilter filter = InjectorLookup.getInstance(this, MyFilterBean.class).get();
        environment.servlets().addFilter("manualFilter", filter)
            .addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
    }
}

Resources

Dropwizard provides AssetsBundle for serving static files from classpath:

bootstrap.addBundle(new AssetsBundle("/assets/app/", "/", "index.html"));

http://localhost:8080/foo.csssrc/main/resources/assets/app/foo.css
http://localhost:8080/src/main/resources/assets/app/index.html

HTML5 routing

But, if you develop SPA application with HTML5 routes, server will not handle these routes properly. Use guicey SPA bundle which adds proper SPA routing support above dropwizard AssetBundle

GuiceBundle.builder()
    .bundles(SpaBundle.app("spaApp", "/assets/app/", "/").build());

http://localhost:8080/src/main/resources/assets/app/index.html
http://localhost:8080/route/pathsrc/main/resources/assets/app/index.html

Templates

Dropwizard provides ViewBundle for handling templates (freemarker and mustache out of the box, more engines could be plugged).

bootstrap.addBundle(new ViewBundle());

Which allows you to serve rendered templates from rest endpoints.

Templates + resources

But it is not quite handful to use it together with static resources (AssetsBundle) because static resources will have different urls (as they are not served from rest).

If you would like to have JSP-like behaviour (when templates and resources live at the same location and so could easily reference each other) - then use guicey GSP bundle (which is actually just a "glue" for dropwizard ViewBundle and AssetsBundle).

com/exmaple/app/
    person.ftl
    foo.ftl
    style.css
<#-- Sample template without model (/foo.ftl) -->
<html>
    <body>        
        <h1>Hello, it's a template: ${12+22}!</h1>
    </body>
</html>
<#-- Template with model, rendered by rest endpoint (/person/) -->
<#-- @ftlvariable name="" type="com.example.views.PersonView" -->
<html>   
    <head>  
        <link href="/style.css" rel="stylesheet">
    </head>
    <body>
        <!-- calls getPerson().getName() and sanitizes it -->
        <h1>Hello, ${person.name?html}!</h1>
    </body>
</html>
public class PersonView extends TemplateView {
    private final Person person;

    public PersonView(Person person) {    
        super('person.ftl');
        this.person = person;
    }

    public Person getPerson() {
        return person;
    }
}
// Path starts with application name  
@Path("/com.example.app/person/")  
@Produces(MediaType.TEXT_HTML)    
// Important marker
@Template
public class PersonPage {      

    @Inject
    private PersonDAO dao;

    @GET  
    @Path("/")
    public PersonView getMaster() {
        return new PersonView(dao.find(1));
    }    

    @GET  
    @Path("/{id}")
    public PersonView getPerson(@PathParam("id") String id) {
        return new PersonView(dao.find(id));
    }   
}
GuiceBundle.builder()                                     
    .bundles(
             // global views support
             ServerPagesBundle.builder().build(),
             // application registration
             ServerPagesBundle.app("com.example.app", "/com/example/app/", "/")   
                                 // rest path as index page
                                 .indexPage("person/")
                                 .build());

Static resource call:

http://localhost:8080/style.csssrc/main/resources/com/example/app/style.css

Direct template call:

http://localhost:8080/foo.ftlsrc/main/resources/com/example/app/foo.ftl

Rest-driven template call:

http://localhost:8080/person/12/rest/com.example.app/person/12

Index page:

http://localhost:8080//rest/com.example.app/person/

Summary

Declaration differences from pure dropwizard views:

  • Model extends TemplateView
  • Rest endpoints always annotated with @Template
  • Rest endpoints paths starts with registered application name (ServerPagesBundle.app("com.example.app") to be able to differentiate rest for different UI applications

Warning

Standard errors handling in views (templates, custom pages) is replaced by custom mechanism, required to implement per-ui-app errors support.