7

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?

Sri9911
  • 1,187
  • 16
  • 32
Almas Abdrazak
  • 3,209
  • 5
  • 36
  • 80

3 Answers3

8

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
  • 8,007
  • 3
  • 37
  • 56
  • It's not what i'm looking for, because I have some business logic in my GlobalExceptionHandler therefore i need to handle exception from this class – Almas Abdrazak Jul 20 '17 at 18:00
  • 3
    I believe this is exactly what will happen. The exception with will set in `setErrorResult` will be processed by your `GlobalExceptionHandler`. – jny Jul 20 '17 at 18:51
  • Thank you, but i found an alternative way =) – Almas Abdrazak Jul 21 '17 at 05:42
3

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

Sri9911
  • 1,187
  • 16
  • 32
Almas Abdrazak
  • 3,209
  • 5
  • 36
  • 80
0

Is there any other possible ways to handle exceptions in DeferredResult in my GlobalExceptionHandler class

I used setErrorResult method as pointed out by @jny

Here is the body of my controller's method:

DeferredResult<Wrapper<Object>> deferredResult = new DeferredResult<>();

ForkJoinPool.commonPool().submit(() -> {
    try {
        Wrapper<Object> response = ...
        deferredResult.setResult(response);
    } catch (Exception e) {
        deferredResult.setErrorResult(e);
    }
});

return deferredResult;

In this case, the exceptions thrown inside new thread, were passed to global @ExceptionHandler

Kacper86
  • 448
  • 1
  • 8
  • 16