I have a service class with #Async method and If it's calling method throwing any exception then the #ControllerAdvice will not call for global exception handling. But for other classes and services it will call advice and sending email properly.
#Service
public class FileScanServiceImpl implements FileScanService {
#Override
#Async
public void scanFileScheduler() throws MQException {
try{
messageProducer.putFileNameToMQ(fileName);
} catch (Exception e) {
ExceptionUtility.handleException(e, currentFile);
}
}
The ExceptionUtility is used for checking instance on exception and doing some functionality there and throwing custom exception.
public static void handleException(Exception e throws MQException {
String errMsg = "";
if (e instanceof MQException) {
// some functionality
throw new MQException(subject, errMsg);
}
}
And this is my #ControlleAdvice
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(MQException.class)
#ResponseBody
public void handleMQException(HttpServletRequest request, MQException ex) {
// send email
}
}
It there any solution for #Async which will call #ControllerAdvice for global exception, also the existing functionality will not break.
#ExceptionHandler was created to catch only "synchronous exceptions". If it had the ability to catch exceptions from asynchronous threads, then when several threads start and if any of them fail, the request to the server would be interrupted completely and the system could remain in an inconsistent state (due to many other active threads generated by this request)
For handling asynchronous exceptions Spring has the AsyncUncaughtExceptionHandler interface:
public class YourAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// Your exception handling logic
}
}
More information can be found here in the Exceptions section: https://www.baeldung.com/spring-async
Related
I have generic question about #ControllerAdvice and #ExceptionHandler. I have a rest controller annotated #RestController that has 2 apis. If argument validation fails, it throws MethodArgumentNotValidException. I created ExceptionHandler to handle this:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResponseEntity<String> handleException(MethodArgumentNotValidException e) throws Exception {
return new ResponseEntity<>(e.getBindingResult().getGlobalError().getDefaultMessage(), HttpStatus.BAD_REQUEST);
}
}
If I want to log something when this exception happens, can I just add line of code before return statement like:
LOG.info("something happened");
Will it log it and then return BAD_REQUEST back to the caller?
If I want to log something when this exception happens, can I just add line of code before return statement like:
LOG.info("something happened");
Will it log it and then return BAD_REQUEST back to the caller?
Yes. That's the purpose of using #ExceptionHandlers. They help to reduce the code to handle exceptions across several rest endpoints defined in your project. This also serves as the single point to log exceptions, thus avoiding this anti pattern:
//BAD
class SomeService {
public SomeEntity someMethod() {
try {
/* stuff... */
} catch (Exception e) {
//No need to log the exception here
log.error("An exception happened", e);
throw e;
}
}
}
Still, you can have some benefits like wrapping the exceptions and rethrow them:
//GOOD
class SomeService {
public SomeEntity someMethod(String param) {
try {
/* stuff... */
} catch (Exception e) {
//You may use this to perform other logic like setting specific message or wrap your exception
log.error("Unexpected behaviour with param {}", param);
throw new MyCustomException("Some message", e);
}
}
}
You can think of the #ExceptionHandler as a giant catch block for all your rest endpoints and a specific type of exception.
Besides, your GlobalExceptionHandler class becomes the component with logic associated to handle every exception thrown in backend and handles how to report that to client side.
java 8, spring, rest
I am trying to capture the Response that comes from exception mapper, and do something with it in the caller which throws the exception. Thanks.
#Provider
public class CustomerExceptionHandler implements ExceptionMapper<CustomerException>
{
#Override
public Response toResponse(CustomerException exception)
{
return Response.status(Status.BAD_REQUEST).entity(CustomerException.getMessage()).build();
}
}
public class CustomerException extends Exception implements Serializable
{
private static final long serialVersionUID = 1L;
public CustomerException() {
super();
}
public CustomerException(String msg) {
super(msg);
}
public CustomerException(String msg, Exception e) {
super(msg, e);
}
}
public class ExceptionDemo{
public void getExceptionResponse(){
//do something
throw new CustomerException("Something is wrong");// CustomerExceptionHandler is going to return me a Response, how can I capture the response here?
//capture response and do something with it
}
}
I'm not sure ExceptionMappers work in the way you think they do.
When some code in the endpoint throws an exception, and this exception percolates all the way out of the endpoint and back into the container itself (Spring in this case), then the registered ExceptionMappers are consulted to see if they match the thrown exception, and the relevant one's public Response toResponse(T e) {} method is called to transform it into a Response.
The ExceptionMapper doen't get called as part of your endpoint code, and you won't be able to take action based on its resultant Response because it hasn't yet been called. You just need to throw the exception out of the endpoint.
This question already has answers here:
Spring Async Uncaught Exception handler
(4 answers)
Closed 3 years ago.
I have a custom exception that I throw in a class that is not caught in another class.
I don't know what's wrong here :
Class MailService.java
#Async
public void sendMail(String to, String subject) throws EmailNotSentException {
throw new EmailNotSentException();
}
Class MailResource.java
#RequestMapping(value = "/mails-envoyes",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<MailEnvoye> createMailEnvoye(#RequestBody MailEnvoye mailEnvoye, HttpServletRequest request) throws URISyntaxException {
try{
mailService.sendMail(to, "subject");
}catch (EmailNotSentException e){
log.debug(e.getLocalizedMessage());
}
}
Exception
public class EmailNotSentException extends MessagingException {
public EmailNotSentException() {
super();
}
public EmailNotSentException(String message) {
super(message);
}
public EmailNotSentException(String message, Exception e) {
super(message, e);
}
}
The issue is probably the #Async annotation (I'm assuming this code runs with Spring) ... as it says on the tin, it run it asynchronously, which means that it will run in a different thread, so the calling method will never receive the Exception.
If you want to handle the exception in the way you have in your code, just remove the #Async annotation.
There are a few articles around which explain how to catch an exception from an async method (google is your friend), and make it available so something else can deal with it, but it's always asynchronously.
I am using Below Custom Exception class in my project
public class BadRequestException extends WebApplicationException {
private static final long serialVersionUID = 1L;
private String message;
public BadRequestException(String message) {
super();
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
I have created a Mapper class also..
public class BadRequestExceptionMapper implements ExceptionMapper<BadRequestException> {
public Response toResponse(BadRequestException brexec) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(brexec.getResponse().getEntity()).type(MediaType.APPLICATION_JSON).build();
}
}
I am calling my service through a Servlet and the Exception is thrown by one of its method but i am not able to catch it in Servlet class.I have used below code to catch the exception..
try{
//Some Business logic then
service.path("restful").path("jwbservice/" + methodName + "/" + id).header("lid", lid).delete(String.class);
}
catch (BadRequestException ex) {
out.println(ex);
}
catch(Exception exe){
out.println(exe);
}
And the service method i have used this code in my Service class which will throw the exception.
#DELETE
#Path("/deleteLink/{id}")
#Produces(MediaType.APPLICATION_JSON)
public String deleteLink(#PathParam("id") int id, #HeaderParam("lid") String lid) throws BadRequestException {
if (id<= 0) {
throw new BadRequestException("Required Parameter: id");
}
//Some Business Logic
}
My Service throw the BadRequestException but in Servlet it is going to Exception catch not in BadRequestException Catch block.
Can any one know what i am doing wrong.
You will never get that exception in your servlet. This is because the servlet is effectively a REST client, and you are invoking a remote resource method to get some data. The resource call will either be successful (and some data will be mapped back), or it will fail and you will get no data (or a client side error).
On a side note, there is a problem in your server side exception mapper. You do not verify that the exception actually has a response entity before calling:
brexec.getResponse().getEntity()
In cases where the exception doesn't have a response the above code will cause a null pointer exception.
Some quick notes:
Exception classes already have a message property. You do not need to define an additional one
Your exception mapper needs to check for a non-existent response property, before trying to do something with it
The resource path in your servlet does not appear to match the server side path. I assume that is a copy/paste error.
I have my own exception "MyOwnException" and throw this exception from my service class
public void service() throws MyOwnException
{
// some code
}
Now I want to catch MyOwnException in an advice and rethrow a brand-new Exception
public class SimpleThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Method method, Object[] args, Object target,
MyOwnException ex) throws Throwable {
throw new Exception("new Description",ex);
}
}
Now, how can I catch the re-thrown Exception from the above Advice SimpleThrowsAdvice?
You should use the Around advice to do that. See here Spring AOP AfterThrowing vs. Around Advice