Dropwizard error messages from Jersey
You need a serialiszer for ErrorMessage. If you use eclipse Strg+Shift+T and search for "JsonProcessingExceptionMapper". This Exception mapper wants to build response with entity ErrorMessage, but you have no mapper for this.
You have two options:
- Remove this exception mapper and add your custom exception mapper with no entity
- You create a message body writer.
Option 1: add this method in your run():
private void removeDefaultExceptionMappers(boolean deleteDefault,Environment environment)
{
if(deleteDefault){
ResourceConfig jrConfig = environment.jersey().getResourceConfig();
Set<Object> dwSingletons = jrConfig.getSingletons();
List<Object> singletonsToRemove = new ArrayList<Object>();
for (Object singletons : dwSingletons) {
if (singletons instanceof ExceptionMapper && !singletons.getClass().getName().contains("DropwizardResourceConfig")) {
singletonsToRemove.add(singletons);
}
}
for (Object singletons : singletonsToRemove) {
LOG.info("Deleting this ExceptionMapper: " + singletons.getClass().getName());
jrConfig.getSingletons().remove(singletons);
}
}
}
This removes all exeption mappers added by default in DW. Now you can add all exeption mappers you really want. In my case:
environment.jersey().register(new ConstraintViolationExceptionMapper());
environment.jersey().register(new CustomJsonProcessingExceptionMapper());
environment.jersey().register(new EarlyEofExceptionMapper());
Now write your own custom CustomJsonProcessingExceptionMapper without entity:
@Provider
public class CustomJsonProcessingExceptionMapper implements ExceptionMapper<JsonProcessingException> {
private static final Logger LOG = LoggerFactory.getLogger(CustomJsonProcessingExceptionMapper.class);
@Override
public Response toResponse(JsonProcessingException exception) {
/*
* If the error is in the JSON generation, it's a server error.
*/
if (exception instanceof JsonGenerationException) {
LOG.warn("Error generating JSON", exception);
return Response.serverError().build();
}
final String message = exception.getOriginalMessage();
/*
* If we can't deserialize the JSON because someone forgot a no-arg constructor, it's a
* server error and we should inform the developer.
*/
if (message.startsWith("No suitable constructor found")) {
LOG.error("Unable to deserialize the specific type", exception);
return Response.serverError().build();
}
/*
* Otherwise, it's those pesky users.
*/
LOG.debug("Unable to process JSON (those pesky users...)", exception);
return Response.status(Response.Status.BAD_REQUEST)
.build();
}
}
Option 2: You create an serializer/message body writer for ErrorMessage. To do so try this:
@Provider
@Produces(MediaType.TEXT_PLAIN)
public class ErrorMessageBodyWriter implements MessageBodyWriter<ErrorMessage> {
private static final Logger LOG = LoggerFactory.getLogger(ErrorMessageBodyWriter.class);
@Override
public boolean isWriteable(
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType)
{
return ValidationErrorMessage.class.isAssignableFrom(type);
}
@Override
public long getSize(
ErrorMessage t,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType)
{
return -1;
}
@Override
public void writeTo(
ErrorMessage t,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException, WebApplicationException
{
String message = t.getMessage();
entityStream.write(message.getBytes(Charsets.UTF_8));
LOG.info(message);
}
}
Add in your run():
// Serializer
environment.jersey().register(new ErrorMessageBodyWriter());
Hope this helps :-)
Just need to specify these headers for your resource so that dropwizard understands which error message builder to use for the response:
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
Dropwizard will log the underlying error at DEBUG level, so you can turn that on in your logging config to see the cause here:
io.dropwizard.jersey.jackson.JsonProcessingExceptionMapper: DEBUG