Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle exception in DeferredResult in Spring Boot?

I have a rest method:

@RequestMapping(value = "wash/washHistory", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public DeferredResult<String> getWashHistory(@RequestParam(value = "sid", required = true, defaultValue = "") String sid,
            HttpServletResponse response, HttpServletRequest request,
            @RequestParam(value = "sort", defaultValue = "") String sortType,
            @RequestParam(value = "order", defaultValue = "") String order,
            @RequestParam(value = "limit", defaultValue = "") String limit,
            @RequestParam(value = "offset", defaultValue = "") String offset) {

        System.out.println("Thread: "+Thread.currentThread());
        final Integer managerId = checkSession(sid);      
        DeferredResult<String> defResult = new DeferredResult<>();
        new Thread(() -> {
                final String result = washController.getWashHistory( managerId, order, sortType, limit, offset);
                defResult.setResult(result);            
        }).start();
    return defResult;
    }

Inside "getWashHistory" I throw the following custom exception:

throw new InvalidUserInputException("Wrong offset", this.getClass().getSimpleName(), "getWashHist", params);

And to handle this exception I'm using the following class:

@ControllerAdvice
@EnableWebMvc
public class GlobalExceptionHandler {
 @ExceptionHandler(value = InvalidUserInputException.class)
    public ResponseEntity<String> invalidUserInputExc(InvalidUserInputException e) {
          logger.log("GMoika", e.error().getClassName(), e.error().getMethodName(), e.error().getParams(), e.error().getCause());
        return ResponseEntity.
                status(HttpStatus.BAD_REQUEST).
                body(e.error().getErrorCode());  
    }
}

It works fine as long as I am not using DeferredResult, but when I want to use non-blocking way ,time out exception occurs. I found one way to fix it:

defResult.onTimeout(new Runnable() {
        @Override
        public void run() {
            defResult.setErrorResult("Explanation goes here.");
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); //or SC_NO_CONTENT
        }
    });

But it's not what I'm looking for because I throw my own exception with specific constructor to write cause inside class where this exception is thrown. Is there any other possible ways to handle exceptions in DeferredResult in my GlobalExceptionHandler class?

like image 297
Almas Abdrazak Avatar asked Jan 04 '23 17:01

Almas Abdrazak


2 Answers

DeferredResult has method setErrorResult which can take Exception and according to the documentation:

The value may be an Exception or Throwable in which case it will be processed as if a handler raised the exception.

like image 160
jny Avatar answered Jan 13 '23 15:01

jny


@jny decision is right in majority situations, but for me I found an alternative way In my rest controller I added the following code:

new Thread(() -> {          
            Thread.currentThread().setUncaughtExceptionHandler(new SeparateThreadsExceptionHandler(defResult));
            final String result = washController.getWashHistory(managerId, order, sortType, limit, offset);
            defResult.setResult(result);
        }).start();

And a SeparateThreadsExceptionHandler class :

public class SeparateThreadsExceptionHandler implements Thread.UncaughtExceptionHandler{
    private DeferredResult<String> dr;
    public SeparateThreadsExceptionHandler(DeferredResult<String> dr){
        this.dr = dr;
    }
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        if(e instanceof InvalidUserInputException){
           InvalidUserInputException throwableException =  (InvalidUserInputException)e;
            dr.setResult(throwableException.error().getErrorCode());
        } else {
            dr.setResult(UnknownException.UNKNOWN_ERROR_CODE);
        }
    }

}

And when my custom exception is thrown I can set some string error message to DeferredResult. In my example it's error code for the front end.

like image 44
Almas Abdrazak Avatar answered Jan 13 '23 14:01

Almas Abdrazak