38
38
import java .util .Arrays ;
39
39
import java .util .Collections ;
40
40
import java .util .HashSet ;
41
- import java .util .IdentityHashMap ;
42
41
import java .util .LinkedList ;
43
42
import java .util .List ;
44
43
import java .util .Objects ;
45
44
import java .util .Optional ;
46
45
import java .util .Queue ;
47
46
import java .util .Set ;
47
+ import java .util .function .Predicate ;
48
48
import java .util .stream .Collectors ;
49
49
50
50
public final class ExceptionsHelper {
@@ -186,27 +186,14 @@ public static <T extends Throwable> T useOrSuppress(T first, T second) {
186
186
* @return Corruption indicating exception if one is found, otherwise {@code null}
187
187
*/
188
188
public static IOException unwrapCorruption (Throwable t ) {
189
- return t == null ? null : doUnwrapCorruption (t , Collections .newSetFromMap (new IdentityHashMap <>()));
190
- }
191
-
192
- private static IOException doUnwrapCorruption (Throwable t , Set <Throwable > seen ) {
193
- do {
194
- if (seen .add (t ) == false ) {
195
- return null ;
196
- }
189
+ return t == null ? null : ExceptionsHelper .<IOException >unwrap (t , logger , cause -> {
197
190
for (Class <?> clazz : CORRUPTION_EXCEPTIONS ) {
198
- if (clazz .isInstance (t )) {
199
- return ( IOException ) t ;
191
+ if (clazz .isInstance (cause )) {
192
+ return true ;
200
193
}
201
194
}
202
- for (Throwable suppressed : t .getSuppressed ()) {
203
- IOException corruptionException = doUnwrapCorruption (suppressed , seen );
204
- if (corruptionException != null ) {
205
- return corruptionException ;
206
- }
207
- }
208
- } while ((t = t .getCause ()) != null );
209
- return null ;
195
+ return false ;
196
+ }).orElse (null );
210
197
}
211
198
212
199
/**
@@ -219,9 +206,11 @@ private static IOException doUnwrapCorruption(Throwable t, Set<Throwable> seen)
219
206
*/
220
207
public static Throwable unwrap (Throwable t , Class <?>... clazzes ) {
221
208
if (t != null ) {
222
- final Set < Throwable > seen = Collections . newSetFromMap ( new IdentityHashMap <>()) ;
209
+ int iterations = 0 ;
223
210
do {
224
- if (seen .add (t ) == false ) {
211
+ ++iterations ;
212
+ if (iterations > MAX_ITERATIONS ) {
213
+ logger .warn ("giving up looking for nested cause" , t );
225
214
return null ;
226
215
}
227
216
for (Class <?> clazz : clazzes ) {
@@ -258,16 +247,10 @@ public static boolean reThrowIfNotNull(@Nullable Throwable e) {
258
247
259
248
static final int MAX_ITERATIONS = 1024 ;
260
249
261
- /**
262
- * Unwrap the specified throwable looking for any suppressed errors or errors as a root cause of the specified throwable.
263
- *
264
- * @param cause the root throwable
265
- * @return an optional error if one is found suppressed or a root cause in the tree rooted at the specified throwable
266
- */
267
- public static Optional <Error > maybeError (final Throwable cause , final Logger logger ) {
268
- // early terminate if the cause is already an error
269
- if (cause instanceof Error ) {
270
- return Optional .of ((Error ) cause );
250
+ @ SuppressWarnings ("unchecked" )
251
+ private static <T extends Throwable > Optional <T > unwrap (Throwable cause , Logger logger , Predicate <Throwable > predicate ) {
252
+ if (predicate .test (cause )) {
253
+ return Optional .of ((T ) cause );
271
254
}
272
255
273
256
final Queue <Throwable > queue = new LinkedList <>();
@@ -277,12 +260,12 @@ public static Optional<Error> maybeError(final Throwable cause, final Logger log
277
260
iterations ++;
278
261
// this is a guard against deeply nested or circular chains of exceptions
279
262
if (iterations > MAX_ITERATIONS ) {
280
- logger .warn ("giving up looking for fatal errors " , cause );
263
+ logger .warn ("giving up looking for nested cause " , cause );
281
264
break ;
282
265
}
283
266
final Throwable current = queue .remove ();
284
- if (current instanceof Error ) {
285
- return Optional .of ((Error ) current );
267
+ if (predicate . test ( current ) ) {
268
+ return Optional .of ((T ) current );
286
269
}
287
270
Collections .addAll (queue , current .getSuppressed ());
288
271
if (current .getCause () != null ) {
@@ -292,6 +275,16 @@ public static Optional<Error> maybeError(final Throwable cause, final Logger log
292
275
return Optional .empty ();
293
276
}
294
277
278
+ /**
279
+ * Unwrap the specified throwable looking for any suppressed errors or errors as a root cause of the specified throwable.
280
+ *
281
+ * @param cause the root throwable
282
+ * @return an optional error if one is found suppressed or a root cause in the tree rooted at the specified throwable
283
+ */
284
+ public static Optional <Error > maybeError (final Throwable cause , final Logger logger ) {
285
+ return unwrap (cause , logger , t -> t instanceof Error );
286
+ }
287
+
295
288
/**
296
289
* See {@link #maybeError(Throwable, Logger)}. Uses the class-local logger.
297
290
*/
0 commit comments