The unofficial Dropwizard 0.7.1 to 0.8.5 upgrade notes
A tale of endless googling, trial and error and dependency nightmare
According to the website, Dropwizard is a Java framework for developing ops-friendly, high-performance, RESTful web services.

As per the Dropwizard 0.8.0 release notes, the most significant change is Jersey upgrade from version 1.18.1 to 2.16, and depending on how heavily you have built on top of Jersey, it could be a smooth or relatively challenging upgrade.
Jersey 1.x — https://github.com/jersey/jersey
Jersey 2.x — https://github.com/eclipse-ee4j/jersey
The Dropwizard 0.7.x to 0.8.x migration guide is very brief https://github.com/dropwizard/dropwizard/wiki/Upgrade-guide-0.7.x-to-0.8.x and was of not much help in my case.
Hopefully, the content of this article, in addition to the migration guide above, would help address a majority of upgrade issues.
Pre-merge, post-merge and testing checklist
If you are upgrading a production-level application, it’s important to make sure that
- There is a sufficient level of unit and integration test coverage
- Performance/load testing pipeline exist
- Some sort of deployment pipeline with canarying
- The ability to release, hotfix, and rollback if and when needed
After the upgrade, here is what I did
- Ensure that unit and integration tests pass locally and in CI server ✔️
- G️et the required pull request reviews ✔️
- Verify that logs are still visible in your log monitoring systems ✔️
- Code freeze on the branch/pull request ✔️
- Run performance/load test before/after the upgrade ✔️
- Run UI automation tests to ensure functionalities are not broken ✔️
- Perform some manual testing of features i.e. SSO, etc. ✔️
- Coordinate with the development teams the merge and deployment to
dev, canary and production
environment date ✔️ - Release to
canary
(if you have canarying) and keep an eye on issues/bugs reported to identify if related to the upgrade ✔️ - Release to
prod
and keep an eye on issues reported and logs ✔️ - When a related issue/bug surfaces, gather the appropriate devs to investigate the issue and decide whether to do a hotfix or the fix can go in the next release ✔️
- For the next few days to few weeks keep an eye on logs, and product issues reported ✔️
Fix the imports
Jersey 1.x package name was com.sun.jersey.*
and in Jersey 2.x it is org.glassfish.jersey.*
and some of the classes are in favor of other options available to us.
com.sun.jersey.api.NotFoundException
→javax.ws.rs.NotFoundException
com.sun.jersey.core.util.Base64
→java.util.Base64
com.sun.jersey.multipart.*
→org.glassfish.jersey.media.multipart.*
com.sun.jersey.api.ConflictException
→javax.ws.rs.ClientErrorException
com.sun.jersey.api.core.ExtendedUriInfo
→org.glassfish.jersey.server.ExtendedUriInfo
com.sun.jersey.api.uri.UriTemplate
→org.glassfish.jersey.uri.UriTemplate
com.sun.jersey.spi.container.ContainerRequestFilter
→javax.ws.rs.container.ContainerRequestFilter
com.sun.jersey.spi.container.ContainerResponseFilter
→javax.ws.rs.container.ContainerResponseFilter
com.sun.jersey.spi.container.ContainerRequest
→javax.ws.rs.container.ContainerRequestContext
com.sun.jersey.spi.container.ContainerResponse
→javax.ws.rs.container.ContainerResponseContext
com.sun.jersey.api.client.Client
→javax.ws.rs.client.*
com.sun.jersey.api.core.ResourceConfig
→org.glassfish.jersey.server.ResourceConfig
com.sun.jersey.api.container.filter.LoggingFilter
→org.glassfish.jersey.filter.LoggingFilter
As shown above, the NotFoundException
is dropped and could be replaced with javax.ws.rs
version, and similarly, in place of ConflictException
, the ClientErrorException
could be used passing it a Response.Status.CONFLICT
type.
As a result of the change from ContainerRequest
to ContainerRequestContext
the request.getRequestUri().getPath()
is not there and initially, I used request.geturiInfo().getPath()
however, after some digging, I found out that with the above change the leading /
is missing and so the right choice was to use request.getUriInfo().getAbsolutePath().getPath()
based on the Javadoc of UriInfo.getAbsolutePath() and requestUri.getPath() of Jersey.
Worth looking into this StackOverflow answer for more Jersey 2.x class names.
Base64 Encoder/Decoder
Jersey 1.x came with a com.sun.jersey.core.util.Base64
and Jersey 2 does not have a replacement, so I switched to using the java.util.Base64
(in Jersey 2 the Base64 class is dropped) one of the concerns was to verify that the output of encoding and especially decoding remains the same.
Jersey 1.x Base64 mentions that it supports RFC2045 and the java.util.Base64 says it supports RFC2045 as well as RFC 4648.
To be sure, wrote a unit test to test byte arrays of various lengths i.e. 0,1,2,3,4
bytes, and some special characters and symbols
< > / \ “ ‘ : ; & ? @ % #
You can find the unit test in this gist.
Dependencies
You can either rely on dropwizard-core
to bring all the compile dependencies or exclude the compile dependency and bring a different version of it. Let’s say you do not wish to bring the same version of hibernate that dropwizard-core
of 0.8.5
brings in which case you exclude the dropwizard-hibernate
from dropwizard-core
and declare it separately with a different version.
In my case, dependencies such as jersey, hibernate, jackson, jetty, shiro and a few others are declared independently, so I had to manually upgrade them.
I have the following dependencies and you might or might not need them all
- Update
dropwizard-core
version to0.8.5
- Update
jeryse
version to2.16
- Update
jetty
version to9.2.9.v20150224
- Update
Jackson
version to2.5.1+
- Update
org.second.dropwizard:dropwizard-shire
to2.0.0
if you use Apache Shiro
If you have dropwizard-hibernate
then for 0.8.5
it would be pulling an older version of org.jadira.usertype
which would result in the following error
java.lang.NoSuchMethodError: org.hibernate.engine.jdbc.spi.JdbcServices.getConnectionProvider()Lorg/hibernate/engine/jdbc/connections/spi/ConnectionProvider
and based on this answer in StackOverflow, I excluded hibernate-core
and org.jaidra.usertype
from dropwizard-hibernate
as shown below
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-hibernate</artifactId>
<version>${dropwizard.version}</version>
<exclusions>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.jadira.usertype</groupId>
<artifactId>usertype.core</artifactId>
</exclusion>
</exclusions>
</dependency>
and brought the two excluded compile dependencies separately, since org.jadira.usertype
bring hibernate-entitymanager
need to exclude that so the version of dropwizard-hibernate
is pulled.
<dependency>
<groupId>org.jadira.usertype</groupId>
<artifactId>usertype.core</artifactId>
<version>4.0.0.GA</version>
<exclusions>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</exclusion>
</exclusions>
</dependency>
If you don’t exclude the hibernate-entitymanager
then you are probably going to see the following error, see this answer
Exception in thread "main" java.lang.NoClassDefFoundError: org/hibernate/engine/jndi/JndiNameException and javax.ws.rs.core.Application.getProperties()Ljava/util/Map
Register MultiPartFeature & LoggingFilter
For Multipart, I suggest looking into this answer in Stackoverflow, and more specifically don’t forget to register the MutliPartFeature.class
as follow
environment.jersey().register(MultiPartFeature.class);
In order to ensure that the inbound/outbound request bodies are logged, you need the following
environment.jersey().register(new LoggingFilter(Logger.getLogger("InboundRequestResponse"), true));
The boolean passed turns on and off the POST entity body that you might not want change to false for production. See http://danofhisword.com/dev/dropwizard/2015/06/29/dropwizard-logging.html
In my case, a Restful web service so all errors are text or JSON with a status code, and without the following, Jersey would try to find servlet error pages and result in 404 for any error.
environment.jersey().getResourceConfig().property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);
Update Jersey 1.x WebClient to 2.x
If you have used Jersey 1.x WebClient to test endpoints then those tests need to be updated
// HTTP GET example// Jersey 1.x
Student result = client.resource("/api/student/1").type(MediaType.APPLICATION_JSON_TYPE).get(Student.class);// Jersey 2.x
Student result = client.target("/api/student/1").request(MediaType.APPLICATION_JSON_TYPE).get(Student.class);
Notice the change from .resource
to .target
and from .type
to .request
. While HTTP GET
is relatively straight forward, HTTP POST
becomes a little more verbose as shown below
// HTTP POST example// Jersey 1.x
Student student = client.resource("/api/student").type(MediaType.APPLICATION_JSON).post(Student.class, new Student());// Jersey 2.x
WebTarget webTarget = client.target("/api/student"); Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON); invocationBuilder.post(Entity.json("..."));
I will leave the HTTP PUT
and HTTP DELETE
for you to lookup.
Diff between com.sun.jersey.api.NotFoundException and javax.ws.rs.NotFoundException
In Jersey 1.x in order to get the message from NotFoundException
you need to call exception.getResponse().getEntity()
whereas in javax.ws.rs.NotFoundException
it’s simply exception.getMessage()
and if you have written a unit test using Jersey 1 NotFoundException
then it’s hard to make use of the Rule
to verify the exception message however, now it's much easier.
JacksonMessageBodyParser Fork
If in you have any endpoint and/or API tests that fall into the following categories
- A
HTTP POST
is passed""
or null instead of{}
- A
HTTP DELETE
is provided a body
Then you would get an exception and that’s because of https://github.com/dropwizard/dropwizard/issues/625 and https://github.com/dropwizard/dropwizard/pull/633. A fix was added in this commit https://github.com/dropwizard/dropwizard/pull/633/commits/4ae991ed37bab64215727b68381be81cc36b45b4
Dropwizard 0.8.5 does not log sub-resource paths in the console on application startup
I created an entry in the dropwizard-dev
Google group forum and with steps on how to reproduce the issue https://groups.google.com/forum/#!topic/dropwizard-dev/G7PdlUWxmsI and have not received a response with an explanation.
I have documented workarounds in the Github https://github.com/rhamedy/dropwizard-sample-app and it does require forking DropwizardResourceConfig
Jetty IOException: Broken pipe and TimeoutException
After upgrading to Dropwizard 0.8.5
two exceptions started showing up and one of them is java.io.IOException: Broken pipe
shown below
java.io.IOException: Broken pipe
at sun.nio.ch.FileDispatcherImpl.writev0(FileDispatcherImpl.java)
at sun.nio.ch.SocketDispatcher.writev(SocketDispatcher.java:51)
at sun.nio.ch.IOUtil.write(IOUtil.java:148)
at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:501)
at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:172)at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:408)
at org.eclipse.jetty.io.WriteFlusher.write(WriteFlusher.java:302)
.
.
.
org.eclipse.jetty.io.EofException: null
at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:192)at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:408)at org.eclipse.jetty.io.WriteFlusher.write(WriteFlusher.java:302)
...
and the other is TimeoutException: Idle timeout expired: 600000/600000 ms
with message
An I/O error has occurred while writing a response message entity to the container output stream.
As for the timeout exception, the explanation given in this https://github.com/eclipse/jetty.project/issues/3907 Github issue might be the case.
In comparison to TimeoutException
, the Broken pipe
the exception happens a lot more often and a majority in the community indicate that it’s safe to ignore and you can read about it in
and
and I have been able to reproduce
If the server side HTTP application is getting Broken Pipe exceptions it just means the client browser has exited/gone to another page/timed out/gone back in the history/whatever. Just forget about it.
by excessive clicking of the front-end and moving away from the page before it loads.
There is also a way to reproduce Broken pipe
exception programmatically, and here is a gist https://gist.github.com/rhamedy/97bd27ac748204cdbb9e439e4d268bdd on how to do it. Comment-out lines 19–23 to observe exception.
Conclusion
This was my first ever attempt at upgrading a production-level application with thousands of users and automated processes consuming the APIs, and I have to admit that the following was a huge confidence boost
- A good level of the unit and integration testing
- A competent group of developers and architects who helped with code reviews and investigation of pre and post-upgrade issues
- The CI/CD pipeline that makes it super easy to release, hotfix and rollback
- Appropriate log monitoring systems i.e. Datadog, Loggly, and Sentry
- A desire and support from the team to upgrade these frameworks
I hope that you find this article helpful 👍
Further Reading
I have also written a few other articles on the following topics, please feel free to read them
- How to Load Test: A developer’s guide to performance testing
- JUnit 4 & 5 Annotations Every Developer Should Know
- Why you should think twice about contributing to Open Source (promotes contribution in contrary to the title)
- Key habits and things I wish I knew earlier as a developer
- A short summary of Java coding best practices
- Encryption and decryption of data based on users password using PBKDF2 and AES