Support direct matching against exceptions in ExceptionTypeFilter
Prior to this commit, ExceptionTypeFilter only supported matching against an exception type. However, most use cases involve matching against an exception instance. Moreover, every use case within the core Spring Framework uses ExceptionTypeFilter to match against concrete exception instances. This commit therefore introduces an overloaded match(Throwable) method in ExceptionTypeFilter in order to provide support for the most common use cases. See gh-35109 Closes gh-35160
This commit is contained in:
parent
33f51b183d
commit
17df4b4c38
|
@ -62,7 +62,7 @@ class CachePutInterceptor extends AbstractKeyCacheInterceptor<CachePutOperation,
|
|||
}
|
||||
catch (CacheOperationInvoker.ThrowableWrapper ex) {
|
||||
Throwable original = ex.getOriginal();
|
||||
if (!earlyPut && operation.getExceptionTypeFilter().match(original.getClass())) {
|
||||
if (!earlyPut && operation.getExceptionTypeFilter().match(original)) {
|
||||
cacheValue(context, value);
|
||||
}
|
||||
throw ex;
|
||||
|
|
|
@ -58,7 +58,7 @@ class CacheRemoveAllInterceptor extends AbstractCacheInterceptor<CacheRemoveAllO
|
|||
}
|
||||
catch (CacheOperationInvoker.ThrowableWrapper ex) {
|
||||
Throwable original = ex.getOriginal();
|
||||
if (!earlyRemove && operation.getExceptionTypeFilter().match(original.getClass())) {
|
||||
if (!earlyRemove && operation.getExceptionTypeFilter().match(original)) {
|
||||
removeAll(context);
|
||||
}
|
||||
throw ex;
|
||||
|
|
|
@ -56,12 +56,12 @@ class CacheRemoveEntryInterceptor extends AbstractKeyCacheInterceptor<CacheRemov
|
|||
}
|
||||
return result;
|
||||
}
|
||||
catch (CacheOperationInvoker.ThrowableWrapper wrapperException) {
|
||||
Throwable ex = wrapperException.getOriginal();
|
||||
if (!earlyRemove && operation.getExceptionTypeFilter().match(ex.getClass())) {
|
||||
catch (CacheOperationInvoker.ThrowableWrapper ex) {
|
||||
Throwable original = ex.getOriginal();
|
||||
if (!earlyRemove && operation.getExceptionTypeFilter().match(original)) {
|
||||
removeValue(context);
|
||||
}
|
||||
throw wrapperException;
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheResultOper
|
|||
if (exceptionCache == null) {
|
||||
return;
|
||||
}
|
||||
if (filter.match(ex.getClass())) {
|
||||
if (filter.match(ex)) {
|
||||
doPut(exceptionCache, cacheKey, ex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ public record MethodRetrySpec(
|
|||
|
||||
MethodRetryPredicate combinedPredicate() {
|
||||
ExceptionTypeFilter exceptionFilter = new ExceptionTypeFilter(this.includes, this.excludes);
|
||||
return (method, throwable) -> exceptionFilter.match(throwable.getClass()) &&
|
||||
return (method, throwable) -> exceptionFilter.match(throwable) &&
|
||||
this.predicate.shouldRetry(method, throwable);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ class DefaultRetryPolicy implements RetryPolicy {
|
|||
|
||||
@Override
|
||||
public boolean shouldRetry(Throwable throwable) {
|
||||
return this.exceptionFilter.match(throwable.getClass()) &&
|
||||
return this.exceptionFilter.match(throwable) &&
|
||||
(this.predicate == null || this.predicate.test(throwable));
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,16 @@ public class ExceptionTypeFilter extends InstanceFilter<Class<? extends Throwabl
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the type of the supplied {@code exception} matches this filter.
|
||||
* @since 7.0
|
||||
* @see InstanceFilter#match(Object)
|
||||
*/
|
||||
public boolean match(Throwable exception) {
|
||||
return match(exception.getClass());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the specified {@code instance} matches the specified
|
||||
* {@code candidate}.
|
||||
|
|
|
@ -33,81 +33,83 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
*/
|
||||
class ExceptionTypeFilterTests {
|
||||
|
||||
ExceptionTypeFilter filter;
|
||||
|
||||
@Test
|
||||
void emptyFilter() {
|
||||
var filter = new ExceptionTypeFilter(null, null);
|
||||
filter = new ExceptionTypeFilter(null, null);
|
||||
|
||||
assertMatches(filter, Throwable.class);
|
||||
assertMatches(filter, Error.class);
|
||||
assertMatches(filter, Exception.class);
|
||||
assertMatches(filter, RuntimeException.class);
|
||||
assertMatches(new Throwable());
|
||||
assertMatches(new Error());
|
||||
assertMatches(new Exception());
|
||||
assertMatches(new RuntimeException());
|
||||
}
|
||||
|
||||
@Test
|
||||
void includes() {
|
||||
var filter = new ExceptionTypeFilter(List.of(FileNotFoundException.class, IllegalArgumentException.class), null);
|
||||
filter = new ExceptionTypeFilter(List.of(FileNotFoundException.class, IllegalArgumentException.class), null);
|
||||
|
||||
assertMatches(filter, FileNotFoundException.class);
|
||||
assertMatches(filter, IllegalArgumentException.class);
|
||||
assertMatches(filter, NumberFormatException.class);
|
||||
assertMatches(new FileNotFoundException());
|
||||
assertMatches(new IllegalArgumentException());
|
||||
assertMatches(new NumberFormatException());
|
||||
|
||||
assertDoesNotMatch(filter, Throwable.class);
|
||||
assertDoesNotMatch(filter, FileSystemException.class);
|
||||
assertDoesNotMatch(new Throwable());
|
||||
assertDoesNotMatch(new FileSystemException("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void includesSubtypeMatching() {
|
||||
var filter = new ExceptionTypeFilter(List.of(RuntimeException.class), null);
|
||||
filter = new ExceptionTypeFilter(List.of(RuntimeException.class), null);
|
||||
|
||||
assertMatches(filter, RuntimeException.class);
|
||||
assertMatches(filter, IllegalStateException.class);
|
||||
assertMatches(new RuntimeException());
|
||||
assertMatches(new IllegalStateException());
|
||||
|
||||
assertDoesNotMatch(filter, Exception.class);
|
||||
assertDoesNotMatch(new Exception());
|
||||
}
|
||||
|
||||
@Test
|
||||
void excludes() {
|
||||
var filter = new ExceptionTypeFilter(null, List.of(FileNotFoundException.class, IllegalArgumentException.class));
|
||||
filter = new ExceptionTypeFilter(null, List.of(FileNotFoundException.class, IllegalArgumentException.class));
|
||||
|
||||
assertDoesNotMatch(filter, FileNotFoundException.class);
|
||||
assertDoesNotMatch(filter, IllegalArgumentException.class);
|
||||
assertDoesNotMatch(new FileNotFoundException());
|
||||
assertDoesNotMatch(new IllegalArgumentException());
|
||||
|
||||
assertMatches(filter, Throwable.class);
|
||||
assertMatches(filter, AssertionError.class);
|
||||
assertMatches(filter, FileSystemException.class);
|
||||
assertMatches(new Throwable());
|
||||
assertMatches(new AssertionError());
|
||||
assertMatches(new FileSystemException("test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void excludesSubtypeMatching() {
|
||||
var filter = new ExceptionTypeFilter(null, List.of(IllegalArgumentException.class));
|
||||
filter = new ExceptionTypeFilter(null, List.of(IllegalArgumentException.class));
|
||||
|
||||
assertDoesNotMatch(filter, IllegalArgumentException.class);
|
||||
assertDoesNotMatch(filter, NumberFormatException.class);
|
||||
assertDoesNotMatch(new IllegalArgumentException());
|
||||
assertDoesNotMatch(new NumberFormatException());
|
||||
|
||||
assertMatches(filter, Throwable.class);
|
||||
assertMatches(new Throwable());
|
||||
}
|
||||
|
||||
@Test
|
||||
void includesAndExcludes() {
|
||||
var filter = new ExceptionTypeFilter(List.of(IOException.class), List.of(FileNotFoundException.class));
|
||||
filter = new ExceptionTypeFilter(List.of(IOException.class), List.of(FileNotFoundException.class));
|
||||
|
||||
assertMatches(filter, IOException.class);
|
||||
assertMatches(filter, FileSystemException.class);
|
||||
assertMatches(new IOException());
|
||||
assertMatches(new FileSystemException("test"));
|
||||
|
||||
assertDoesNotMatch(filter, FileNotFoundException.class);
|
||||
assertDoesNotMatch(filter, Throwable.class);
|
||||
assertDoesNotMatch(new FileNotFoundException());
|
||||
assertDoesNotMatch(new Throwable());
|
||||
}
|
||||
|
||||
|
||||
private static void assertMatches(ExceptionTypeFilter filter, Class<? extends Throwable> candidate) {
|
||||
assertThat(filter.match(candidate))
|
||||
.as("filter '" + filter + "' should match " + candidate.getSimpleName())
|
||||
private void assertMatches(Throwable candidate) {
|
||||
assertThat(this.filter.match(candidate))
|
||||
.as("filter '" + this.filter + "' should match " + candidate.getClass().getSimpleName())
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
private static void assertDoesNotMatch(ExceptionTypeFilter filter, Class<? extends Throwable> candidate) {
|
||||
assertThat(filter.match(candidate))
|
||||
.as("filter '" + filter + "' should not match " + candidate.getSimpleName())
|
||||
private void assertDoesNotMatch(Throwable candidate) {
|
||||
assertThat(this.filter.match(candidate))
|
||||
.as("filter '" + this.filter + "' should not match " + candidate.getClass().getSimpleName())
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue