1

I am using resilience4j's TimeLimiter to control timeout requests for a RestTemplate call. I am throwing a custom exception if the response.getBody() is null but, resilience4j's always throwing exception provided in getorElseThrow. how can I throw a custom exception here?

Try.ofCallable(methodWhichThrowsCustomException).getOrElseThrow(throwable -> {throw new ApplicationException(HttpStatus.REQUEST_TIMEOUT,
                    ErrorConstant.ERROR_CODE_REQUEST_TIMEOUT, ErrorConstant.ERROR_MESSAGE_TIME_OUT);
        });
    }

Here i am trying throw the exception that the "methodWhichThrowsCustomException" throws, but i am not able to do that it only throws the exception the thrown here.

2 Answers2

1

I don't know resilience4j but I'm pretty sure you are using getOrElseThrow wrong. The syntax you most likely want is this:

Try.ofCallable(methodWhichThrowsCustomException())
   .getOrElseThrow(throwable -> new ApplicationException(HttpStatus.REQUEST_TIMEOUT, ErrorConstant.ERROR_CODE_REQUEST_TIMEOUT, ErrorConstant.ERROR_MESSAGE_TIME_OUT));

Note that you don't need to throw inside getOrElseThrow, you only need to return the new exception to throw, Vavr will take care of throwing it.

Sir4ur0n
  • 1,753
  • 1
  • 13
  • 24
0

Vavr's Try.of().getOrElseThrow()

See the blog Vavr One Log 03 - A Safe Try:

T getOrElseThrow(Supplier)

getOrElseThrow expects an exception-supplier as argument. This may be a lambda, method-reference, or any functional. Its purpose is to map the thrown exception to your instance of a custom Exception. For example:

Try.ofCallable(methodWhichThrowsCustomException)
    .getOrElseThrow(throwable -> new CustomerException(
        HttpStatus.REQUEST_TIMEOUT,
        ErrorConstant.ERROR_CODE_REQUEST_TIMEOUT, 
        ErrorConstant.ERROR_MESSAGE_TIME_OUT,
        throwable
    ))
}

Issue

Note: we use throwable -> new .. inside parentheses, nothing more. The exception instance is just created (and returned implicitly), we do not throw it - we supply it to vavr which throws then.

You passed a consumer instead. The consuming lambda { throw new ...; } is throwing a new Exception immediately instead of simply mapping it with { return new CustomException("Custom message", thrown); }.

See also

Exceptions from TimeLimiter

throw the exception that the "methodWhichThrowsCustomException" throws

Assume you have a method:

public String sayHelloWorld(boolean shouldThrow) throws CustomException {
    if (shouldThrow) throw new CustomException("Hello Exception");
    return "Hello World!";
}

You pass this to the TimeLimiter When it executes and it was not able to complete successfully within time, it throws either of two exceptions

  1. when exceeding the time-limit, Resilience4J throws a TimeoutException:

When a timeout occurs, it cancels the running Future before throwing a TimeoutException.

  1. for any other ExecutionException, like when your method throws a custom exception, it will find that cause and rethrow this exception. See TimeLimiterImpl, catch-block, line 57.

Then you should be able to catch it:

try {
    // The blocking variant which is basically future.get(timeoutDuration, MILLISECONDS)
    String result = timeLimiter.executeFutureSupplier(
  () -> CompletableFuture.supplyAsync(() -> sayHelloWorld(true)));
    log.info(result);
} catch (Exception e) {
    log.error(e); // or print to debug, handle, etc.
}

or using Vavr's Try:

String result = Try.of(() -> timeLimiter.executeFutureSupplier(
  () -> CompletableFuture.supplyAsync(() -> sayHelloWorld(true))) // method forced to throw
).getOrElseThrow(throwable -> throwable); // will throw as is (without mapping to another exception)
log.info(result);

See also:

Implementing Timeouts with Resilience4j - Reflectoring

hc_dev
  • 8,389
  • 1
  • 26
  • 38