Skip to content

Commit 17df4b4

Browse files
committed
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
1 parent 33f51b1 commit 17df4b4

File tree

8 files changed

+57
-45
lines changed

8 files changed

+57
-45
lines changed

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CachePutInterceptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public CachePutInterceptor(CacheErrorHandler errorHandler) {
6262
}
6363
catch (CacheOperationInvoker.ThrowableWrapper ex) {
6464
Throwable original = ex.getOriginal();
65-
if (!earlyPut && operation.getExceptionTypeFilter().match(original.getClass())) {
65+
if (!earlyPut && operation.getExceptionTypeFilter().match(original)) {
6666
cacheValue(context, value);
6767
}
6868
throw ex;

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CacheRemoveAllInterceptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ protected CacheRemoveAllInterceptor(CacheErrorHandler errorHandler) {
5858
}
5959
catch (CacheOperationInvoker.ThrowableWrapper ex) {
6060
Throwable original = ex.getOriginal();
61-
if (!earlyRemove && operation.getExceptionTypeFilter().match(original.getClass())) {
61+
if (!earlyRemove && operation.getExceptionTypeFilter().match(original)) {
6262
removeAll(context);
6363
}
6464
throw ex;

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CacheRemoveEntryInterceptor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ protected CacheRemoveEntryInterceptor(CacheErrorHandler errorHandler) {
5656
}
5757
return result;
5858
}
59-
catch (CacheOperationInvoker.ThrowableWrapper wrapperException) {
60-
Throwable ex = wrapperException.getOriginal();
61-
if (!earlyRemove && operation.getExceptionTypeFilter().match(ex.getClass())) {
59+
catch (CacheOperationInvoker.ThrowableWrapper ex) {
60+
Throwable original = ex.getOriginal();
61+
if (!earlyRemove && operation.getExceptionTypeFilter().match(original)) {
6262
removeValue(context);
6363
}
64-
throw wrapperException;
64+
throw ex;
6565
}
6666
}
6767

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CacheResultInterceptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ protected void cacheException(@Nullable Cache exceptionCache, ExceptionTypeFilte
9292
if (exceptionCache == null) {
9393
return;
9494
}
95-
if (filter.match(ex.getClass())) {
95+
if (filter.match(ex)) {
9696
doPut(exceptionCache, cacheKey, ex);
9797
}
9898
}

spring-context/src/main/java/org/springframework/resilience/retry/MethodRetrySpec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public MethodRetrySpec(MethodRetryPredicate predicate, long maxAttempts, Duratio
6565

6666
MethodRetryPredicate combinedPredicate() {
6767
ExceptionTypeFilter exceptionFilter = new ExceptionTypeFilter(this.includes, this.excludes);
68-
return (method, throwable) -> exceptionFilter.match(throwable.getClass()) &&
68+
return (method, throwable) -> exceptionFilter.match(throwable) &&
6969
this.predicate.shouldRetry(method, throwable);
7070
}
7171

spring-core/src/main/java/org/springframework/core/retry/DefaultRetryPolicy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class DefaultRetryPolicy implements RetryPolicy {
5959

6060
@Override
6161
public boolean shouldRetry(Throwable throwable) {
62-
return this.exceptionFilter.match(throwable.getClass()) &&
62+
return this.exceptionFilter.match(throwable) &&
6363
(this.predicate == null || this.predicate.test(throwable));
6464
}
6565

spring-core/src/main/java/org/springframework/util/ExceptionTypeFilter.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ public ExceptionTypeFilter(@Nullable Collection<? extends Class<? extends Throwa
6464
}
6565

6666

67+
/**
68+
* Determine if the type of the supplied {@code exception} matches this filter.
69+
* @since 7.0
70+
* @see InstanceFilter#match(Object)
71+
*/
72+
public boolean match(Throwable exception) {
73+
return match(exception.getClass());
74+
}
75+
76+
6777
/**
6878
* Determine if the specified {@code instance} matches the specified
6979
* {@code candidate}.

spring-core/src/test/java/org/springframework/util/ExceptionTypeFilterTests.java

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -33,81 +33,83 @@
3333
*/
3434
class ExceptionTypeFilterTests {
3535

36+
ExceptionTypeFilter filter;
37+
3638
@Test
3739
void emptyFilter() {
38-
var filter = new ExceptionTypeFilter(null, null);
40+
filter = new ExceptionTypeFilter(null, null);
3941

40-
assertMatches(filter, Throwable.class);
41-
assertMatches(filter, Error.class);
42-
assertMatches(filter, Exception.class);
43-
assertMatches(filter, RuntimeException.class);
42+
assertMatches(new Throwable());
43+
assertMatches(new Error());
44+
assertMatches(new Exception());
45+
assertMatches(new RuntimeException());
4446
}
4547

4648
@Test
4749
void includes() {
48-
var filter = new ExceptionTypeFilter(List.of(FileNotFoundException.class, IllegalArgumentException.class), null);
50+
filter = new ExceptionTypeFilter(List.of(FileNotFoundException.class, IllegalArgumentException.class), null);
4951

50-
assertMatches(filter, FileNotFoundException.class);
51-
assertMatches(filter, IllegalArgumentException.class);
52-
assertMatches(filter, NumberFormatException.class);
52+
assertMatches(new FileNotFoundException());
53+
assertMatches(new IllegalArgumentException());
54+
assertMatches(new NumberFormatException());
5355

54-
assertDoesNotMatch(filter, Throwable.class);
55-
assertDoesNotMatch(filter, FileSystemException.class);
56+
assertDoesNotMatch(new Throwable());
57+
assertDoesNotMatch(new FileSystemException("test"));
5658
}
5759

5860
@Test
5961
void includesSubtypeMatching() {
60-
var filter = new ExceptionTypeFilter(List.of(RuntimeException.class), null);
62+
filter = new ExceptionTypeFilter(List.of(RuntimeException.class), null);
6163

62-
assertMatches(filter, RuntimeException.class);
63-
assertMatches(filter, IllegalStateException.class);
64+
assertMatches(new RuntimeException());
65+
assertMatches(new IllegalStateException());
6466

65-
assertDoesNotMatch(filter, Exception.class);
67+
assertDoesNotMatch(new Exception());
6668
}
6769

6870
@Test
6971
void excludes() {
70-
var filter = new ExceptionTypeFilter(null, List.of(FileNotFoundException.class, IllegalArgumentException.class));
72+
filter = new ExceptionTypeFilter(null, List.of(FileNotFoundException.class, IllegalArgumentException.class));
7173

72-
assertDoesNotMatch(filter, FileNotFoundException.class);
73-
assertDoesNotMatch(filter, IllegalArgumentException.class);
74+
assertDoesNotMatch(new FileNotFoundException());
75+
assertDoesNotMatch(new IllegalArgumentException());
7476

75-
assertMatches(filter, Throwable.class);
76-
assertMatches(filter, AssertionError.class);
77-
assertMatches(filter, FileSystemException.class);
77+
assertMatches(new Throwable());
78+
assertMatches(new AssertionError());
79+
assertMatches(new FileSystemException("test"));
7880
}
7981

8082
@Test
8183
void excludesSubtypeMatching() {
82-
var filter = new ExceptionTypeFilter(null, List.of(IllegalArgumentException.class));
84+
filter = new ExceptionTypeFilter(null, List.of(IllegalArgumentException.class));
8385

84-
assertDoesNotMatch(filter, IllegalArgumentException.class);
85-
assertDoesNotMatch(filter, NumberFormatException.class);
86+
assertDoesNotMatch(new IllegalArgumentException());
87+
assertDoesNotMatch(new NumberFormatException());
8688

87-
assertMatches(filter, Throwable.class);
89+
assertMatches(new Throwable());
8890
}
8991

9092
@Test
9193
void includesAndExcludes() {
92-
var filter = new ExceptionTypeFilter(List.of(IOException.class), List.of(FileNotFoundException.class));
94+
filter = new ExceptionTypeFilter(List.of(IOException.class), List.of(FileNotFoundException.class));
9395

94-
assertMatches(filter, IOException.class);
95-
assertMatches(filter, FileSystemException.class);
96+
assertMatches(new IOException());
97+
assertMatches(new FileSystemException("test"));
9698

97-
assertDoesNotMatch(filter, FileNotFoundException.class);
98-
assertDoesNotMatch(filter, Throwable.class);
99+
assertDoesNotMatch(new FileNotFoundException());
100+
assertDoesNotMatch(new Throwable());
99101
}
100102

101103

102-
private static void assertMatches(ExceptionTypeFilter filter, Class<? extends Throwable> candidate) {
103-
assertThat(filter.match(candidate))
104-
.as("filter '" + filter + "' should match " + candidate.getSimpleName())
104+
private void assertMatches(Throwable candidate) {
105+
assertThat(this.filter.match(candidate))
106+
.as("filter '" + this.filter + "' should match " + candidate.getClass().getSimpleName())
105107
.isTrue();
106108
}
107109

108-
private static void assertDoesNotMatch(ExceptionTypeFilter filter, Class<? extends Throwable> candidate) {
109-
assertThat(filter.match(candidate))
110-
.as("filter '" + filter + "' should not match " + candidate.getSimpleName())
110+
private void assertDoesNotMatch(Throwable candidate) {
111+
assertThat(this.filter.match(candidate))
112+
.as("filter '" + this.filter + "' should not match " + candidate.getClass().getSimpleName())
111113
.isFalse();
112114
}
113115

0 commit comments

Comments
 (0)