JAX-RS using exception mappers
54,530
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 {
...
}
}
}
Author by
Jordan Allan
Updated on January 12, 2020Comments
-
Jordan Allan over 4 years
I have read that I can create an implementation of
javax.ws.rs.ext.ExceptionMapper
that will map a thrown application exception to aResponse
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(); } }
-
Jordan Allan almost 14 yearsThank-you Blaise, that worked. Why must I wrap InvalidDataException in a PersistenceException though? According to one of the books I have, it says the JAX-RS runtime will handle any thrown exception if an exception mapper is registered. In my case, I registered an exception mapper for InvalidDataException...
-
bdoughan almost 14 yearsThe JPA implementation is going to catch InvalidDataException and wrap it in PersistenceException. Then your JAX-RS implementation is going to get a PersistenceException. This is why you need to unwrap it.
-
bdoughan almost 14 years
-
Jordan Allan almost 14 yearsMakes perfect sense now. Thanks!
-
Catalin Morosan over 11 yearsWhat should go into the else statement? If there is a different exception, I want the jax-rs implementation to handle it with its default behavior.
-
bdoughan over 11 years@kaciula - What goes into the
else
statement depends on your application. Ultimately you could build different types ofResponse
objects based on the different types of nested exceptions. -
ksl almost 8 years@BlaiseDoughan Why does the
InvalidDataException
get wrapped in aPersistenceException
?