-
Couldn't load subscription status.
- Fork 38.8k
Description
When a method executes code that may throw an exception, it is usual to hide the exception if is not expected:
public void foo() {
try {
// method calls
} catch(BusinessException /*parent class of business exception*/e) {
throw e; // message should be shown (or translated and shown) to the user
} catch(Throwable t /*not a business exception, which means it is not expected */) {
// log information about t,
// home made retry (for tx serialization failure for instance) or:
throw new RuntimException("an error occured"); // because e may show sensitive details that should not exit the method
}
}However if the exception is not a BusinessException its type would not visible to the retry interceptor, which makes fine grained interception configuration impossible. If we want to use @Retryable the best we can do here is excludes = BusinessException.class but then NPE will trigger retry.
How about a rethrow property ?:
@Retryable(includes = CannotAcquireLockException.class, rethrow = RuntimeException.class)
public void foo() {
try {
// method calls
} catch(Throwable t) {
// apply log policy depending on the exception (business exception thrown by our code vs unexpected)
// throw t;
}
}retry will apply for CannotAcquireLockException, and if the max attemps is reached the exception rethrown by the interceptor to the caller would be an instance of RuntimeException.
Or:
@Retryable(includes = CannotAcquireLockException.class, rethrow = @Rethrow(type = RuntimeException.class, message = "..."))
An other option, an ExceptionTranslator (Function<Throwable, Throwable>) could be used:
@Retryable(includes = CannotAcquireLockException.class, translator = MyExceptionTranslator.class)
public void foo() {
try {
// method cals
} catch(Throwable t) {
// apply log policy depending on the exception (business exception thrown by our code vs unexpected)
// throw t;
}
}The retry interceptor would call the translator and eventually throw what the translation function returns.