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 productionenvironment 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
prodand 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.
As shown above, the
NotFoundExceptionis dropped and could be replaced with
javax.ws.rs version, and similarly, in place of
ClientErrorException could be used passing it a
As a result of the change from
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.
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.
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.
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
0.8.5 brings in which case you exclude the
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
2.0.0if 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
and based on this answer in StackOverflow, I excluded
dropwizard-hibernate as shown below
and brought the two excluded compile dependencies separately, since
hibernate-entitymanager need to exclude that so the version of
dropwizard-hibernate is pulled.
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
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.
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
.target and from
.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.
If in you have any endpoint and/or API tests that fall into the following categories
HTTP POSTis passed
""or null instead of
HTTP DELETEis 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
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 org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:172)at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:408)
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
what is the meaning of Broken pipe Exception?
Thanks for contributing an answer to Stack Overflow! Please be sure to answer the question. Provide details and share…
How to fix java.net.SocketException: Broken pipe?
I’d the same problem while I was developing a simple Java application that listens on a specific TCP. Usually, I had no…
Any fixes for issue org.eclipse.jetty.io.EofException · Issue #4464 · eclipse/jetty.project
You can’t perform that action at this time. You signed in with another tab or window. You signed out in another tab or…
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.
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 👍
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