I’ve been going through the process of moving my Spring MVC web application to DropWizard. One benefit of this technology I hope to utilize is the ability to release an application and not affect cohabitating applications within a web container.
Starting Point
Before the migration, it’s important to know the structure of my application. I use annotation-based beans, and component-scan
to construct them.
- src
- main
- java
- test
- main
- web
- resources
- WEB-INF
- app.properties
- database-context.xml
- root-context.xml
- servlet-context.xml
- web.xml
- target
The configuration XML files are all in WEB-INF
, and Maven packaged it as a war for Tomcat to deploy.
Diving into DropWizard, I suggest you follow their tutorial, writing the Configuration and Application in your code.
Migration
Because DropWizard runs the application through a main
method, the Spring dependency-injection requirements needs a new approach. Tutorials would indicate migrating completely to a
Configuration bean. My solution was a little easier.
- Consolidate `root` and `servlet` contexts into one context, `applicationContext` we’ll call it.
- The new `applicationContext` can still import resources, but targeting those resources needs to respect the classpath existing within DropWizard.
- Create a `Configuration` bean looking like this:
@Configuration @ImportResource({ "file:applicationContext.xml" }) public class MyAppSpringConfig { }
The result of this configuration is that it will import the configuration in the `applicationContext`, thus alleviating the work required to move all configuration into a Java bean.
4. Create the DropWizard Configuration (pretty much empty, so not including it) and Application classes. My application follows the example in Jacek99’s GitHub project.
public class MyApplication extends Application<MyConfiguration> { public static void main(String[] args) throws Exception { new MyApplication().run(args); } @Override public void initialize(Bootstrap<MyConfiguration> bootstrap) { String resourcePath = "/assets", uriPath = "/example"; bootstrap.addBundle(new ConfiguredAssetsBundle(resourcePath, uriPath)); } public void run(MyConfiguration configuration, Environment environment) { AnnotationConfigWebApplicationContext rootCtx = new AnnotationConfigWebApplicationContext(); AnnotationConfigWebApplicationContext servletCtx = new AnnotationConfigWebApplicationContext(); rootCtx.refresh(); rootCtx.getBeanFactory().registerSingleton("configuration", configuration); rootCtx.registerShutdownHook(); rootCtx.start(); // the real main app context has a link to the parent context servletCtx.setParent(rootCtx); servletCtx.register(MyConfiguration.class); WebAppContext webApp = new WebAppContext("localhost:8080", "/"); servletCtx.setServletContext(webApp.getServletContext()); servletCtx.refresh(); servletCtx.registerShutdownHook(); servletCtx.start(); // add health checks Map<String, HealthCheck> healthChecks = servletCtx.getBeansOfType(HealthCheck.class); for (Map.Entry<String, HealthCheck> entry : healthChecks.entrySet()) { environment.healthChecks().register(entry.getKey(), entry.getValue()); } // add resources Map<String, Object> resources = servletCtx.getBeansWithAnnotation(Path.class); for (Map.Entry<String, Object> entry : resources.entrySet()) { environment.jersey().register(entry.getValue()); } // add tasks Map<String, Task> tasks = servletCtx.getBeansOfType(Task.class); for (Map.Entry<String, Task> entry : tasks.entrySet()) { environment.admin().addTask(entry.getValue()); } // Add the Spring servlet context to the servlets in DropWizard environment.servlets().addServletListeners(new ContextLoaderListener(servletCtx)); } }
Controllers
DropWizard will not use view resolvers like Spring, nor will jersey dispatch to @RequestMapping
s. So the next step is to migrate Spring annotations to Javax WS annotations.
@RequestMapping
becomes@Path
– and the full path is required, not just a suffixRequestMethod.GET
becomes@GET
(etc)@RequestParam
becomes@QueryParam
@PathVariable
becomes@PathParam
@ModelAttribute
is removed, but the method annotated with@Consumes(MediaType.APPLICATION_JSON)
(media type can vary)
Ending File Structure
After the migration, I moved the configuration xml files to a resources
directory beneath main, and view resources inside the web
dir.
- src
- main
- java
- resources
- test
- main
- web
- css
- js
- img
- target
Hurdles
No resources for rendering
Note that DropWizard will not support Java Servlet Pages, as it serves static resources. That’s okay, DropWizard supports some template views and other Javascript frameworks. Web resources can be externally accessible (outside of the jar) using ConfiguredAssetsBundle, and setting the directory name to replace /example
with in the yaml:
assets: overrides: /example: web
Cannot resolve ServletContextResource without ServletContext (WebAppInitializer)
The ApplicationContext requires a ServletContext. To remedy this error, create a WebAppContext to grab the ServletContext, and put it on the ApplicationContext:
AnnotationConfigWebApplicationContext servletCtx = new AnnotationConfigWebApplicationContext(); WebAppContext webApp = new WebAppContext("localhost:8080", "/"); servletCtx.setServletContext(webApp.getServletContext());
Hi Ben,
Thanks for this post. I found your article, basically trying to add a web context to my existing dropwizard rest application.
I am trying to implement what you’ve done here but do not get it fully. Would you have a repo to share where I could also see the actual resource files you use (*applicationContext.xml, etc…) ?