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?
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.
@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.
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