I have read that I can create an implementation of javax.ws.rs.ext.ExceptionMapper
that will map a thrown application exception to a Response
object.
I've created a simple example which throws an exception if the phone length is greater than 20 characters when persisting the object. I am expecting the exception to be mapped to an HTTP 400 (Bad Request) response; however, I am receiving an HTTP 500 (Internal Server Error) with the following exception:
java.lang.ClassCastException: com.example.exception.InvalidDataException cannot be cast to java.lang.Error
What am I missing? Any advice is greatly appreciated.
Exception mapper:
@Provider public class InvalidDataMapper implements ExceptionMapper<InvalidDataException> { @Override public Response toResponse(InvalidDataException arg0) { return Response.status(Response.Status.BAD_REQUEST).build(); } }
Exception class:
public class InvalidDataException extends Exception { private static final long serialVersionUID = 1L; public InvalidDataException(String message) { super(message); } ... }
Entity class:
@Entity @Table(name="PERSON") @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="ID") private Long id; @Column(name="NAME") private String name; @Column(name="PHONE") private String phone; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @PrePersist public void validate() throws InvalidDataException { if (phone != null) { if (phone.length() > 20) { throw new InvalidDataException("Phone number too long: " + phone); } } } }
Service:
@Path("persons/") @Produces(MediaType.APPLICATION_XML) @Consumes(MediaType.APPLICATION_XML) @Stateless public class PersonResource { @Context private UriInfo uriInfo; @PersistenceContext(name="simple") private EntityManager em; @POST public Response createPerson(JAXBElement<Person> personJaxb) { Person person = personJaxb.getValue(); em.persist(person); em.flush(); URI personUri = uriInfo.getAbsolutePathBuilder(). path(person.getId().toString()).build(); return Response.created(personUri).build(); } }
Thrown exceptions are handled by the JAX-RS runtime if you have registered an exception mapper. Exception mappers can convert an exception to an HTTP response. If the thrown exception is not handled by a mapper, it is propagated and handled by the container (i.e., servlet) JAX-RS is running within.
ExceptionMapper is a contract for a provider that maps Java exceptions to Response object. An implementation of ExceptionMapper interface must be annotated with @Provider to work correctly.
Exception HandlerThe @ExceptionHandler is an annotation used to handle the specific exceptions and sending the custom responses to the client. Define a class that extends the RuntimeException class. You can define the @ExceptionHandler method to handle the exceptions as shown.
Is InvalidDataException getting wrapped in a PersistenceException? Maybe you could do something like the following:
@Provider public class PersistenceMapper implements ExceptionMapper<PersistenceException> { @Override public Response toResponse(PersistenceException arg0) { if(arg0.getCause() instanceof InvalidDataException) { return Response.status(Response.Status.BAD_REQUEST).build(); } else { ... } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With