Issue with ExceptionMapper in Jersey - java

I have two classes that implements ExceptionMapper interface.
IllegalArgumentExceptionMapper to handle IllegalArgumentException:
#Slf4j
#Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
#Override
public Response toResponse(IllegalArgumentException exception) {
log.info("IllegalArgumentExceptionMapper!");
Error error =
Error.builder()
.statusCode(HttpStatus.SC_BAD_REQUEST)
.statusDescription(exception.getLocalizedMessage())
.errorMessage(exception.getMessage())
.build();
return Response.status(Response.Status.BAD_REQUEST)
.entity(error)
.type(MediaType.APPLICATION_JSON)
.build();
}
}
GenericExceptionMapper is an ExceptionMapper that I want to use as the default ExceptionMapper when an exception is not mapped to any of my other specific ExceptionMapper classes. Here it is:
#Slf4j
#Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable ex) {
log.info("GenericExceptionMapper!");
Response.StatusType type = getStatusType(ex);
Error error = Error.builder()
.statusCode(type.getStatusCode())
.statusDescription(type.getReasonPhrase())
.errorMessage(ex.getLocalizedMessage())
.build();
return Response.status(error.getStatusCode())
.entity(error)
.type(MediaType.APPLICATION_JSON)
.build();
}
private Response.StatusType getStatusType(Throwable ex) {
if (ex instanceof WebApplicationException) {
return((WebApplicationException)ex).getResponse().getStatusInfo();
} else {
return Response.Status.INTERNAL_SERVER_ERROR;
}
}
}
However, when I try to throw an IllegalArgumentException, with:
throw new IllegalArgumentException("Just a normal IllegalArgumentException!");
I see that GenericExceptionMapper instead of IllegalArgumentExceptionMapper is being used.(I see "GenericExceptionMapper!" in the log).
Any idea what went wrong?
Some observations
If I delete GenericExceptionMapper, IllegalArgumentExceptionMapper is still not being called. So I think there is an issue for my IllegalArgumentExceptionMapper implementation.
If I modify IllegalArgumentExceptionMapper with public class IllegalArgumentExceptionMapper implements ExceptionMapper<RuntimeException> and throw new RuntimeException, then I see that IllegalArgumentExceptionMapper is being used.

Related

ExceptionHandler not working with Spring Cloud and Aws Lambda

I created a function on AWS Lambda with Spring Cloud and java 11.
I'm trying to catch the exceptions thrown with #ExceptionHandler but it's not working.
I'm using an API Gateway Http Api as a trigger
Is there any specific way to catch these exceptions with Spring Cloud?
Function
#Component
public class TestFunction implements Function<APIGatewayV2HTTPEvent, APIGatewayV2HTTPResponse> {
#Override
public APIGatewayV2HTTPResponse apply(APIGatewayV2HTTPEvent event) {
throw new TestException();
}
}
Handle
#ControllerAdvice
public class TestExceptionHandler {
#ExceptionHandler(value = {TestException.class})
public APIGatewayV2HTTPResponse handleTestException(TestException e) {
return APIGatewayV2HTTPResponse.builder()
.withStatusCode(422).build();
}
}
Handle - second attempt as suggested by #kladderradatsch
#ControllerAdvice(basePackageClasses = TestFunction.class)
public class TestExceptionHandler extends ResponseEntityExceptionHandler {
#ResponseBody
#ExceptionHandler(value = {TestException.class})
public ResponseEntity<?> handleTestException(TestException ex) {
return new ResponseEntity<>(new MyErrorBody(400, ex.getMessage()), HttpStatus.BAD_REQUEST);
}
}
Exception
public class TestException extends RuntimeException {
public TestException() {
super("Error Test.");
}
}
I tried to catch the exception in several different ways, including adding a #ResponseStatus(HttpStatus.BAD_REQUEST) to the exception, but that didn't work either.

Ambiguous #ExceptionHandler method for HttpMessageNotReadableException

In my #RestController I'm successfully handling JSONParse exceptions coming from #RequestBody (for example, a String wrongly entered into an Integer field). This is the code:
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler({ HttpMessageNotReadableException.class })
public ValidationError handleException(HttpMessageNotReadableException ex) {
if (ex.getCause() instanceof InvalidFormatException) {
...
} else {
throw ex;
}
}
Now I want to move this to a #ControllerAdvice to be used by many controllers. Here it is:
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler({ HttpMessageNotReadableException.class })
public ValidationError handleException(HttpMessageNotReadableException ex) {
if (ex.getCause() instanceof InvalidFormatException) {
...
} else {
throw ex;
}
}
But Spring complains with the following:
Ambiguous #ExceptionHandler method mapped for [class org.springframework.http.converter.HttpMessageNotReadableException]: {public Object foo.bar.RestExceptionHandler.handleException(org.springframework.http.converter.HttpMessageNotReadableException), public final org.springframework.http.ResponseEntity org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest) throws java.lang.Exception}
I can't override ResponseEntityExceptionHandler.handleException because it's final. What other options are there?
Using Spring Boot 2.4.3.
I can't override ResponseEntityExceptionHandler.handleException because it's final
You're supposed to override the protected ResponseEntity<Object> handleHttpMessageNotReadable(...) method instead for custom error handling.
You should not extend from ResponseEntityExceptionHandler class.
Check this answer https://stackoverflow.com/a/71119336/4757784

ConflictException thrown not being catched by ConflictExceptionMapper

I'm new to jersey, and I want to throw a ConflictException(Custom) in my Service,
and catch it to give a response.
Below is my code..
public class ConflictException extends ClientErrorException {
public ConflictException() {
super(Response.Status.CONFLICT); // 409
}
}
Then I want to use a ExceptionMapper to send a proper Response.
#Provider
public class ConflictExceptionMapper implements ExceptionMapper<ConflictException> {
#Override
public Response toResponse(ConflictException exception) {
return Response.status(Response.Status.CONFLICT).entity(new ResponseMessage( Collections.emptyList(), OperationResultStatus.Conflict,
exception.getMessage())).build();
}
}
Somehow this Mapper is not being triggered.
What am I doing wrong....
Update!!! Snipped of my main class included.
final ResourceConfig rc = new ResourceConfig().packages(packages)
// Now you can expect validation errors to be sent to the client.
.property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true)
// #ValidateOnExecution annotations on subclasses won't cause errors.
.property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true)
// Further configuration of ResourceConfig.
//.property(ServerProperties.PROCESSING_RESPONSE_ERRORS_ENABLED, true)
.register(new ClassBinder())
.register(json)
.register(ConstraintViolationExceptionMapper.class)
.register(ConflictExceptionMapper.class);

Hystrix - how to register ExceptionMapper

My Hystrix/Feign app makes calls to other web services.
I would like to propagate error codes/messages from these web services.
I implemented ErrorDecoder, which correctly decodes exceptions returned and rethrow them.
Unfortunately these Exceptions are wrapped by HystrixRuntimeException and the JSON returned in not what I want (generic error message, always 500 http status).
Most likely I need an ExceptionMapper, I created one like this:
#Provider
public class GlobalExceptionHandler implements
ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable e) {
System.out.println("ABCD 1");
if(e instanceof HystrixRuntimeException){
System.out.println("ABCD 2");
if(e.getCause() != null && e.getCause() instanceof HttpStatusCodeException)
{
System.out.println("ABCD 3");
HttpStatusCodeException exc = (HttpStatusCodeException)e.getCause();
return Response.status(exc.getStatusCode().value())
.entity(exc.getMessage())
.type(MediaType.APPLICATION_JSON).build();
}
}
return Response.status(500).entity("Internal server error").build();
}
}
Unfortunately this code is not being picked-up by my application (debug statements are not visible in logs).
How could I register it with my Application?
I couldn't make use of an ExceptionMapper.
I solved this problem using ResponseEntityExceptionHandler.
Here is the code:
#EnableWebMvc
#ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(HystrixRuntimeException.class)
#ResponseBody
ResponseEntity<String> handleControllerException(HttpServletRequest req, Throwable ex) {
if(ex instanceof HystrixRuntimeException) {
HttpStatusCodeException exc = (HttpStatusCodeException)ex.getCause();
return new ResponseEntity<>(exc.getResponseBodyAsString(), exc.getStatusCode());
}
return new ResponseEntity<String>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}

How to have two ControllerAdvice in the same SpringMvc application

I would like to manage Exception thrown by simple Controller or RestController in two ways:
1) html redirection
2) Json error
I tested the code below :
#ControllerAdvice(annotations = Controller.class)
public class ExceptionHandlerController
{
#ExceptionHandler(Exception.class)
public ModelAndView handleException(HttpServletRequest _req, Exception _ex)
{
K_LOGGER.info("test");
return new ModelAndView();
}
}
#ControllerAdvice(annotations = RestController.class)
public class ExceptionHandlerRestController
{
#ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(HttpServletRequest _req, Exception _ex)
{
return new ResponseEntity<>("test", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#RestController
public class GreetingController
{
#RequestMapping("/greetingexception")
public Greeting greetingException(#RequestParam(value = "name", defaultValue = "World") String name)
throws Exception
{
throw new Exception();
}
}
It doesn't work properly, I always pass by ExceptionHandlerController but not by ExceptionHandlerRestController.
I think it's because #RestController inherit of #Controller.
Do you have a other solution?
Try to add #Order(Ordered.HIGHEST_PRECEDENCE) annotation to rest exception handler. It may helps you.
eg04lt3r answer is correct, just though that more details might be useful for someone.
In case when you have global #ControllerAdvice and want to handle some exception in a different way in one of your Controllers you need to set #Order(Ordered.HIGHEST_PRECEDENCE) on the #ControllerAdvice which should have higher priority.
For example:
#ControllerAdvice
public class GeneralExceptionHandler {
#ExceptionHandler(Exception.class)
protected ResponseEntity<Error> handleException(Exception ex) {
...
}
}
#ControllerAdvice(assignableTypes = MyController.class)
#Order(Ordered.HIGHEST_PRECEDENCE)
public class MyExceptionHandler {
#ExceptionHandler(Exception.class)
protected ResponseEntity<Error> handleException(Exception ex) {
...
}
}
#Order is needed because on startup one of the handlers will register with higher order automatically, anyway and your exception handling will become unpredictable. For example I recently saw a case when if you start an app using bootRun gradle task MyExceptionHandler was primary, but when started as jar GeneralExceptionHandler was primary.

Categories