Over the last few months, I’ve had the opportunity to do a lot of work with DropWizard, an awesome Java web framework for developing RESTful web services.
I was recently prototyping a small application, and I wanted to serve static files directly from DropWizard. While this isn’t what DropWizard is primarily designed to do, I didn’t want to go through the process of setting up another web server just to serve static assets for a prototype. While DropWizard has some out-of-the-box support for serving static assets, I found the documentation surrounding it to be incomplete and confusing. It actually took me a couple hours of debugging to figure out how to do exactly what I wanted, So I thought I would document what I learned here.
My end goal was to serve a small single page JavaScript app on the root of my DropWizard application, while having my DropWizard REST endpoints available at /api
. The limited documentation around serving assets in DropWizard suggest that setting up an AssetsBundle for /
and changing the application context path in your config file should be enough to achieve this, but that did not work for me. I had to do the following steps:
1. Include the dropwizard-assets Maven dependency.
While the documentation make it appear that the AssetsBundle is part of core DropWizard, it is actually in its own separate dependency. I had to add the following to my pom file:
io.dropwizard
dropwizard-assets
${dropwizard.version}
2. Add an AssetsBundle to my application.
This is pretty much as described in the documentation. An AssetBundle goes in the initialize
method of your DropWizard application and takes three parameters: the path to the assets you want to serve, the path to serve those assets on, and the file to serve by default. My AssetsBundle looked like this:
@Override
public void initialize(Bootstrap myConfigurationBootstrap) {
...
myConfigurationBootstrap.addBundle(new AssetsBundle("/assets", "/", "index.html"));
...
}
3. Configure Jersey to serve my resources at /api.
The docs make it appear that you can do this by changing applicationContextPath
in your config file, but I didn’t have any luck with that. Instead I had to add line to the run
method of my application to set the Jersey URL pattern like so:
@Override
public void run(myConfiguration configuration, Environment environment) throws Exception {
...
environment.jersey().setUrlPattern("/api/*");
...
}
With these three steps together, I was able to accomplish what I wanted and serve both my JavaScript app and my REST backend from the same DropWizard application.
Awesome. Exactly what I’ve been looking for. Thanks alot!
Thanks. It worked perfectly for me.
This was very helpful. Thanks a lot
Gracias… worked as advertised; save hours of debugging…
The docs on serving assets are a bit misleading. Thanks a lot for posting this!
There’s a bit of a difference in the approaches though. The documentation lists this (in 0.7.1, now in 0.8.0 it notes “rootPath” – see the bottom of this post):
server:
type: simple
applicationContextPath: /application/*
This configures DW to use a simple server which just listens on one particular port, rather than a default server, which has multiple “connectors”, each of which can listen to a certain port.
Then, it sets the application context path of the (simple) server, in this case Jetty. So that means that the application context path of the container (Jetty) becomes “/application/*”, and everything served by this Jetty will be served under this.
Your solution on the other hand only changes the configuration of Jersey, the JAX-RS implementation. So only Jersey’s JAX-RS endpoints will be available under the configured path, while other things might still be available under “/” (imagine you add some custom oldschool Servlet).
In 0.8.0, it seems like “rootPath” is only for Jersey, while “applicationContextPath” is the Jetty context path. So now “rootPath” should accomplish the same as setting it in code.
That is cool, I would much rather be able to have my jersey root URL in a config file. I haven’t had a chance to use 0.8 at all yet.
Hi… I am currently working on 0.8 version of drop wizard. I have created a folder under resouces called as ‘images’. I will be storing the user images at this location. But I am not able to access the folder via URL. Can you help?
I’m trying it with DW 0.8.1 now:
I creates index.html in a new folder assets under src/main/resources
1) When following your post, i.e.
a) no change in the config file
b) register assets:
public void initialize(Bootstrap bootstrap) {
bootstrap.addBundle(new AssetsBundle(“/assets”, “/”, “index.html”));
}
c) change the API endpoint:
public void run(HelloWorldConfiguration conf, Environment env) throws Exception {
env.jersey().setUrlPattern(“/api/*”);
…
==> result: IllegalStateException: Multiple servlets map to path: /*
2) When following the official doc:
a) assets bundle registered like in the previous step
b) no programatic change to the API endpoint
c) added to the config:
server:
type: simple
rootPath: /app/*
==> results:
a) it starts
b) http://localhost:8080/assets: 404
c) Weird: during start, DW says that it registered e.g. “GET /app/users”. And when I go there: 404
Very weird.
@Radek
I also tried service static assets with 0.8.1 and came to following conclusion: just setting rootPath you need access your REST services like /application/app/users – and your assets with /application/assets/… The additional “application” comes from the default configuration for the simple server “applicationContextPath: /application”, see https://dropwizard.github.io/dropwizard/manual/configuration.html
I was able to get the desired behaviour (resources in /api and assets in /) with following settings in the yml:
server:
type: simple
rootPath: /api/*
applicationContextPath: /
HTH
I am using 0.8.1, and found if I put the following code in `initialize`, it will work. But if
I put them in `run` id DOES NOT work.
““java
bootstrap.addBundle(new AssetsBundle(“/assets/js”, “/js”, null, “js”));
bootstrap.addBundle(new AssetsBundle(“/assets/css”, “/css”, null, “css”));
““
This does not work anymore for 0.9.2. The trick with the config mentioned by Matthias works as well as this code in the run() method:
final DefaultServerFactory serverFactory =
(DefaultServerFactory) configuration.getServerFactory();
serverFactory.setApplicationContextPath(“/”);
serverFactory.setJerseyRootPath(“/api”);
Change DefaultServerFactory to SimpleServerFactory if you are running a simple server.
previous solutions didn’t work for me either but kurt’s works perfectly for me. i wish it was something a bit cleaner looking.