• Posted by Intent Media 02 Dec
  • 0 Comments

Upgrading Dropwizard 0.6 to 0.7

At Intent Media we started using Dropwizard last year when creating an external API. Dropwizard is a great lightweight Java framework (they really exist!) for building RESTful web services, tying together Jetty, Jersey, Jackson, JDBI and a powerful metrics library.   

Everything was working great with the API in production until a few months ago, when we needed to run our application on both http and https to process SSL requests. Unfortunately, with Dropwizard 0.6 this wasn’t possible so we decided to upgrade to the next version, 0.7. Dropwizard 0.7 isn’t a drop-in replacement for 0.6 and we couldn’t find a decent resource online, so we decided to write this guide based on our upgrade experience.

The changes we had to make to upgrade to 0.7 were the result of two changes to Dropwizard.  First, the resources moved to new packages.  Second, core Dropwizard objects were renamed.  Third, the way that the application is configured through code has changed.  

Structural Changes 

Class packages have all changed

Our previous ivy.xml file was quite sparse, but in 0.7 there are more distinct dependences.  More importantly, the package names changed from com.yammer.dropwizard and com.codahale.metrics to io.dropwizard and io.dropwizard.metrics

0.6
<dependency org="com.yammer.dropwizard" name="dropwizard-client" rev="0.6.2"/>		    
<dependency org="com.yammer.dropwizard" name="dropwizard-core" rev="0.6.2"/>        
<dependency org="com.codahale.metrics" name="metrics-core" rev="3.0.1"/>
<dependency org="com.codahale.metrics" name="metrics-jvm" rev="3.0.1"/>
<dependency org="com.codahale.metrics" name="metrics-jersey" rev="3.0.1"/>
<dependency org="com.yammer.dropwizard" name="dropwizard-jdbi" rev="0.6.2"/>
<dependency org="com.yammer.dropwizard" name="dropwizard-testing" rev="0.6.2"/>
0.7
<dependency org="io.dropwizard" name="dropwizard-client" rev="0.7.1"/>
<dependency org="io.dropwizard" name="dropwizard-core" rev="0.7.1"/>
<dependency org="io.dropwizard" name="dropwizard-configuration" rev="0.7.1"/>
<dependency org="io.dropwizard" name="dropwizard-jersey" rev="0.7.1"/>
<dependency org="io.dropwizard" name="dropwizard-jetty" rev="0.7.1"/>
<dependency org="io.dropwizard" name="dropwizard-jackson" rev="0.7.1"/>
<dependency org="io.dropwizard" name="dropwizard-validation" rev="0.7.1"/>
<dependency org="io.dropwizard" name="dropwizard-util" rev="0.7.1"/>
<dependency org="io.dropwizard.metrics" name="metrics-core" rev="3.1.0"/>
<dependency org="io.dropwizard.metrics" name="metrics-jvm" rev="3.1.0"/>
<dependency org="io.dropwizard.metrics" name="metrics-jersey" rev="3.1.0"/>
<dependency org="io.dropwizard" name="dropwizard-jdbi" rev="0.7.1"/>
<dependency org="io.dropwizard" name="dropwizard-testing" rev="0.7.1"/>

Services are now applications

What was previously referred to as a service is now an application

0.6: public class ApiService extends Service<ApiServiceConfiguration>{}
0.7: public class ApiApplication extends Application<ApiApplicationConfiguration>{}

New configuration file structure

Previously, the configuration had http as a top level configuration item, but now it’s handled under server with the applicationConnectors: configuration. Additionally, logging changed such that the appenders are separate and not limited to console and file logging.

0.6
http:
  port: 8281
  adminPort: 8282
  requestLog:
    console:
      enabled: false
    file:
      enabled: true
      currentLogFilename: ./log/requests.log
      archivedLogFilenamePattern: ./log/requests.%d.log.gz

logging:
  level: INFO
  loggers:
    "com.intentmedia": DEBUG
  console:
    enabled: true
  file:
    enabled: true
    logFormat: "[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %msg%n%throwable"
    currentLogFilename: ./log/api.log
    archivedLogFilenamePattern: ./log/api.%d.log.gz
    archivedFileCount: 50
server:
  applicationConnectors:
    - type: http
      port: 8281
  requestLog:
    appenders:
      - type: file
        currentLogFilename: ./log/requests.log
        archivedLogFilenamePattern: ./log/requests.%d.log.gz
        archivedFileCount: 50

logging:
  level: INFO
  loggers:
    "com.intentmedia": DEBUG
 appenders:
   - type: file
     logFormat: "[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %msg%n%throwable"
     currentLogFilename: ./log/api.log
     archivedLogFilenamePattern: ./log/api.%d.log.gz
   - type: console
     threshold: ALL
     target: stdout
     logFormat: "[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %msg%n%throwable"

Code wise, after resolving the new package paths, the main change was in our ApiApplication where the application is setup. Before, resources and health checks were added directly to the environment.   Now, they’re handled by Jersey and the HealthCheckRegistry.  These changes are in the ApiApplication.run() method:

    0.6
    environment.addProvider(new InstrumentedResourceMethodDispatchAdapter(metricRegistryFactory.create("ResourceStatistics", configuration.getMetrics().getInterval())));
environment.addResource(new UserResource(configuration, httpClient));
environment.addHealthCheck(new HealthyHealthCheck());
    0.7
    environment.jersey().register(new InstrumentedResourceMethodDispatchAdapter(metricRegistryFactory.create("ResourceStatistics", configuration.getMetrics().getInterval())));
environment.jersey().register(new UserResource(configuration, httpClient));
environment.healthChecks().register("healthy", new TemplateHealthCheck());

Gotchas 

As with any large version change there were also a number of gotchas that took us some time to resolve:

  1. ‘metrics’ is now a reserved configuration word. If you have a top level configuration value of ‘metrics’, you’ll need to change it.

  2. HttpClientBuilder has new requirements: environment and user agent (the argument to .build()) are required.

  3. jdbi isn’t as tolerant of repeated connection opening and closing. In an integration junit test, we previously ran jdbi.open() in the @Before method and jdbi.close() in the @After method.  In 0.7 this caused the dreaded “too many open connections” error after enough unit tests ran. The solution is to use jdbi.onDemand(Dao.class).  Additionally, if you are doing any jdbi.execute() calls, an easy way to do this is:

jdbi.withHandle(new HandleCallback() {
    @Override
    public Void withHandle(Handle handle) throws Exception {
        List tableNames = testDataFixture.findAllTables();
        for (String tableName : tableNames) {
            handle.execute(String.format("TRUNCATE TABLE %s", tableName));
        }
        return null;
    }
});

Bonus

SSL with Dropwizard

This whole upgrade process was required so that we could run http and https at the same time, so I would feel bad leaving out the details on how to run Dropwizard with https. Instructions on how to generate a keystore can be found here, with the important command being

keytool -genkey -alias mydomain -keyalg RSA -keystore keystore.jks -keysize 2048

Here is the relevant section of the configuration file for SSL:

server:
  applicationConnectors:
    - type: http
      port: 8281
    - type: https
      port: 8284
      keyStorePath: ./bin/api.keystore
      keyStorePassword: <password>
      certAlias: api
      validateCerts: false
  adminConnectors:
    - type: http
      port: 8282

Wrap Up

At Intent Media we love using Dropwizard and are enjoying the benefits of having upgraded to 0.7.  Upgrading to 0.7 will make future upgrades to 0.8 and beyond easier and will allow us to continue enjoying the ability to rapidly create great REST-ful web services.  Keep up to date on Dropwizard developments at the discussion forum on Google groups, and if you find this all interesting come work with us, and help us write more awesome code.

Tom Allen is a Software Engineer at Intent Media, where he writes a lot of Java and some Ruby.  When not upgrading frameworks he can be found running, cooking and learning to dive.  Infrequently attended to online presences include Twitter and Github.

Post Comments 0