Alex Theedom
Alex Theedom

Reputation: 1662

JAX-RS ExceptionMapper throws MessageBodyProviderNotFoundException

Using JAX-RS, I have successfully implemented an ExceptionMapper for Exceptions that do not require a more sophisticated response than an HTTP status code, as follows.

@Provider 
public class ISBNNotFoundManager implements ExceptionMapper<ISBNNotFoundException>{

  @Override
  public Response toResponse(ISBNNotFoundException exception) {
      return Response.status(NOT_FOUND).build();
  }
}

This works as expected.

However, I want to respond with something more useful when bean validation fails. The follow code snippet results in a MessageBodyProviderNotFoundException.

@Provider
public class ConstraintViolationExceptionMapper implements 
                     ExceptionMapper<ConstraintViolationException> {

  @Override
  @Produces(MediaType.APPLICATION_JSON)
  public Response toResponse(ConstraintViolationException exception) {

      final Map<String, String> errorResponse =
        exception.getConstraintViolations()
        .stream()
        .collect(
          Collectors.toMap(o -> o.getPropertyPath().toString(), o -> o.getMessage()));


      return Response.status(Response.Status.BAD_REQUEST).entity(errorResponse).build();
    }

}

When a bean validation occurs the response includes the HTTP response code 500 and the root cause is given as follow:

org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException:
MessageBodyWriter not found for media type=application/json, 
type=class java.util.HashMap, genericType=class java.util.HashMap.

What I have tried that didn't work:

What I tried the DID work:

Then in the toResponse method I wrap the map in the DataIntegrityValidation POJO like so and add it to the response object.

    DataIntegrityValidation dataIntegrityValidation = 
       new DataIntegrityValidation();
         dataIntegrityValidation.setErrorResponse(errorResponse);

    return
      Response.status(Response.Status.BAD_REQUEST)
      .entity(dataIntegrityValidation).build();

This gives the following JSON:

{
  "errorResponse": {
    "entry": [
      {
        "key": "saveBook.arg0.description",
        "value": "size must be between 100 and 2147483647"
      },
      {
        "key": "saveBook.arg0.published",
        "value": "must be in the past"
      },
      {
        "key": "saveBook.arg0.link",
        "value": "must match \"^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w \\.-]*)*\\/?$\""
      }
    ]
  }
}

I can live with this but would really like to know why it cannot handle the Map even though it is wrapped in the Generic Entity.

All responses welcome.

Upvotes: 0

Views: 342

Answers (1)

ritesh.garg
ritesh.garg

Reputation: 3943

The reason the marshalling failed for both Map and GenericEntity is because there is no JAXB definition associated with them. And when you wrapped the map in a POJO annotated with @XmlRootElement; it was able to marshal it correctly.

Upvotes: 1

Related Questions