Dropwizard provides automatic validation of Jersey resource method parameters by simply adding the @Valid annotation. For example, in a method to save a new Person object, you might have code like:


@POST
public Response createPerson(@Valid Person person) {
    Person savedPerson = save(person);

    URI location = UriBuilder.fromResource(PersonResource.class)
            .path(savedPerson.getId().toString())
            .build();

    return Response.created(location).entity(savedPerson).build();
}

By adding @Valid to the person argument, Dropwizard ensures that the Person object will be validated using Hibernate Validator. The Person object will be validated according to the constraints defined on the Person class, for example maybe the @NotEmpty annotation is on first and last name properties. If the object passes validation, method execution continues and the logic to save the new person takes place. If validation fails, however, Dropwizard arranges for a 422 (Unprocessable Entity) response to be sent back to the client, and the resource method is never actually invoked. This is convenient, as it means you don't need any conditional logic in resource methods to manually check if an object is valid. Under the covers, Dropwizard registers its own custom provider class, JacksonMessageBodyProvider, which uses Jackson to parse request entities into objects and perform validation on the de-serialized entities.

Out of the Dropwizard box this automatic validation "just works" due to the above-mentioned JacksonMessageBodyProvider. (For this post, we are assuming Dropwizard 0.8.2, which uses Jersey 2.19) It worked for us just fine, until on one service it simply stopped working entirely. In other words, no validation took place and therefore any objects, valid or not, were being passed into resource methods. Since resource method code assumes objects have been validated already, this causes downstream problems. In our case, it manifested in HibernateExceptions being thrown when data accesss code tried to persist the (not validated) objects.

This was quite perplexing, and to make a (very long) debuggging story short, it turned out that someone had added one specific dependency in the Maven pom, which triggered auto-discovery of the JacksonFeature via the JacksonAutoDiscoverable. The dependency that had been added (indirectly, more on that later) was:


<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.19</version>
</dependency>

If you look in the jersey-media-json-jackson-2.19.jar file, there are only five classes. But the upshot is that this JAR specifies auto disovery for Jackson via the Auto-Discoverable Features mechanism, which causes the JacksonFeature class to register JacksonJaxbJsonProvider as both a MessageBodyReader and a MessageBodyWriter. And due to the vagaries of the way Jersey orders the message body readers, that provider ends up as the first available MessageBodyReader when processing requests, which in turn means the Dropwizard JacksonMessageBodyProvider never gets executed, and as a result no validation is performed!

For some code spelunking, check out the WorkerComparator class in MessageBodyFactory (in Jersey) which is used when sorting readers and writers via a call to Collections.sort(). The Javadoc for the comparator states "Pairs are sorted by distance from required type, media type and custom/provided (provided goes first)." In particular the last bit of that sentence is key, provided goes first - this means the auto-discovered feature (JacksonJaxbJsonProvider) takes precedence over the custom provider registered by Dropwizard (JacksonMessageBodyProvider).

Of course now that we know what is going on, the solution is pretty easy:

Make sure you don't have the jersey-media-json-jackson dependency, either directly or via a transitive dependency.

In our case it had actually come in via a Maven transitive dependency, which made tracking it down harder. An easy way to see exactly what dependencies exist and where they are coming from you can use mvn dependency:tree to display the entire dependency tree for your application.

Ultimately, while Jersey provides the auto disovery mechanism, I still prefer explicit configuration so it is very clear exactly what features are present. Dropwizard abstracts some of this from us, i.e. it registers the JacksonMessageBodyProvider in the AbstractServerFactory class (in the createAppServlet method), but since Dropwizard is mostly "just code" it is much easier to deterministically know what it is and isn't doing. So if you suddenly experience a lack of validation in Dropwizard, make sure the jersey-media-json-jackson dependency is not present. If that doesn't work, then you need to figure out what other MessageBodyReader is taking precedence, determine its origin, and eliminate it!

Sample code for this post can be found on GitHub.

2 Comments