I’ve been using the Dropwizard Java framework for about a year. It’s a great web application stack for making RESTful service with Java.
I recently ran into a situation where I wanted to add custom servlets to a Dropwizard application. Specifically, I wanted to add a Jersey servlet to the admin port of the application, to support some low level testing and admin functions. I also needed this servlet to support some of the built in Dropwizard things, like automatic serialization with Jackson and hibernate support.
Figuring out how to wire all of this up required a fun romp through some scattered internet forums and a lot of Dropwizard source code. In the hopes that it might help others, I though I would share what I learned here.
First off, here is all of the code needed to accomplish our goal.
DropwizardResourceConfig jerseyConfig = new DropwizardResourceConfig(environment.metrics());
JerseyContainerHolder servletContainerHolder = new JerseyContainerHolder(new ServletContainer(jerseyConfig));
environment.admin().addServlet(name, servletContainer.getContainer()).addMapping("/custom/*");
final FilterHolder holder = new FilterHolder(new GzipFilterFactory().build());
environment.getAdminContext().addFilter(holder, mountPoint, EnumSet.allOf(DispatcherType.class));
jerseyConfig.getSingletons().add(new JacksonMessageBodyProvider(Jackson.newObjectMapper(), environment.getValidator()));
jerseyConfig.getSingletons().add(new UnitOfWorkResourceMethodDispatchAdapter(hibernateBundle.getSessionFactory()));
jerseyConfig.getSingletons().add(new AdminResource(new AdminDAO()));
Now, let’s go through the code and break down each piece.
1. Create new jersey servlet
The first thing to do is actually create the servlet container, which is exposed by Jersey. It only needs a configuration which is conveniently provided by Dropwizard.
DropwizardResourceConfig jerseyConfig = new DropwizardResourceConfig(environment.metrics());
JerseyContainerHolder servletContainerHolder = new JerseyContainerHolder(new ServletContainer(jerseyConfig));
The configuration provided by Dropwizard enables lots of useful features, out of the box, like logging of resource endpoints and exception mappers.
2. Add the servlet to the dropwizard environment and map it
environment.admin().addServlet(name, servletContainer.getContainer()).addMapping("/custom/*);
3. Enable gzip
We need to add gzip filter so that incoming gzipped requests can be decoded.
final FilterHolder holder = new FilterHolder(new GzipFilterFactory().build());
environment.getAdminContext().addFilter(holder, mountPoint, EnumSet.allOf(DispatcherType.class));
4. Enable Jackson
Next, configure our new servlet is to add Jackson support for serializing and deserializing data. Again, through the magic of Dropwizard’s clean abstractions, this can be accomplished with a one liner.
jerseyConfig.getSingletons().add(new JacksonMessageBodyProvider(Jackson.newObjectMapper(), environment.getValidator()));
5. Add unit of work dispatchers for hibernate (if you are using hibernate)
I make heavy use of Dropwizard’s hibernate integration, which wraps all of the database connection setup and transaction work into the convenient UnitOfWork
annotation. In order to make this annotation work, we have to add a special resource method dispatcher with our hibernate session factory.
jerseyConfig.getSingletons().add(new UnitOfWorkResourceMethodDispatchAdapter(hibernateBundle.getSessionFactory()));
6. Register resources
Now we can register our Jersey resource classes, just like for our main jersey servlet on our application.
jerseyConfig.getSingletons().add(new AdminResource(new AdminDAO()));
With that, we now have a fully running jersey servlet on the admin port of our application that we can use to do complex maintenance or testing operations. When you start up Dropwizard, you should even see the route listing for your custom servlet in the console output.
In DW 0.8.1 this doesn’t work. The set returned by jerseyConfig.getSingletons() is unmodifiable. I’m hoping you’ve done some more prowling in the bowels of DW?
The answer is pretty simple. Just use
jerseyConfig.register(new AdminResource(new AdminDAO()));
The paths reported on service start omit the /custom segment of the path, but that’s no big deal.
It should be ‘servletContainerHolder’ instead of ‘servletContainer’ in line 4 of the code.