Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding specific type of custom exception on all inner exceptions

Tags:

java

java-8

I am trying to recursively find all inner exceptions (getCause's) from a top level exception...of a specific instance type.

public class MyCustomRunTimeException extends RuntimeException {

    public MyCustomRunTimeException() {
    }

    public MyCustomRunTimeException(Exception innerException) {
        super(innerException);
    }
}

Here is what I've tried:

and my early "find" method:

private void findAllSpecificTypeOfInnerExceptions(Exception ex)
{

            Collection<MyCustomRunTimeException> MyCustomRunTimeExceptions = Stream.iterate(ex, Throwable::getCause)
                    .filter(element ->
                            element != null
                                    && element instanceof MyCustomRunTimeException
                    )
                    .map(obj -> (MyCustomRunTimeException) obj)
                    .collect(Collectors.toList());

}

It is not working. :( I've tried several other things (not shown yet)... I'll post them as "appends" to this question if I get anything that doesn't throw an exception. Not working.... I'm getting NullPointers exceptions. and (depending on my tweaks) java.lang.reflect.InvocationTargetException exception.

Here is some examples that would find 1:N "matches".

    Exception exampleOne = new MyCustomRunTimeException();

    Exception exampleTwo = new Exception(new MyCustomRunTimeException());

    Exception exampleThree =new Exception(new Exception(new MyCustomRunTimeException()));

    Exception exampleFour =new Exception(new Exception(new MyCustomRunTimeException(new ArithmeticException())));
like image 859
granadaCoder Avatar asked Aug 02 '19 15:08

granadaCoder


3 Answers

This is a case where I really think it's easiest doing the recursive bit in a loop, rather than using streams entirely:

List<Throwable> exlist = new ArrayList<>();
while(ex.getCause()!=null) {
    exlist.add(ex.getCause());
    ex = ex.getCause();
}
exlist = exlist.stream().filter(e -> e instanceof MyCustomRunTimeException).collect(Collectors.toList());

There are better options in Java 9+, and if you use external libraries, but not really in core Java 8 (hacks do exist, but that makes it much less clear than the above, IMHO, and I really don't see that it's worth introducing an external library just for this purpose.)

Or this variation:

private <T extends Throwable> Collection<T> aggregateSubClassExceptions(Throwable inputEx, Class<T> type) {

    Collection<T> returnItems = new ArrayList<>();
    Throwable exc = inputEx;
    while (exc != null) {
        if (type.isInstance(exc)) {
            returnItems.add(type.cast(exc));
        }
        exc = exc.getCause();
    }

    return returnItems;
}

This code was getting the specific type AND any subclasses:

    Collection<MyCustomRunTimeException> testItOutCollection;



    Exception exampleOne = new MyCustomRunTimeException();
    Exception exampleTwo = new Exception(new MyCustomRunTimeException());
    Exception exampleThree = new Exception(new Exception(new MyCustomRunTimeException()));
    Exception exampleFour = new Exception(new Exception(new MyCustomRunTimeException(new ArithmeticException())));
    MyCustomRunTimeException exampleFive = new MyCustomRunTimeException(new MySubMyCustomRunTimeException(new MyCustomRunTimeException(new ArithmeticException())));

    testItOutCollection = this.aggregateSubClassExceptions(exampleOne, MyCustomRunTimeException.class);
    testItOutCollection = this.aggregateSubClassExceptions(exampleTwo, MyCustomRunTimeException.class);
    testItOutCollection = this.aggregateSubClassExceptions(exampleThree, MyCustomRunTimeException.class);
    testItOutCollection = this.aggregateSubClassExceptions(exampleFour, MyCustomRunTimeException.class);
    testItOutCollection = this.aggregateSubClassExceptions(exampleFive, MyCustomRunTimeException.class);
like image 73
Michael Berry Avatar answered Oct 07 '22 20:10

Michael Berry


The problem is that Stream.iterate produces an infinite stream. It never knows when to stop, even if the cause is null.

You can pass a stopping condition in the form of takeWhile but it is only available in Java 9+. See also: Limit a stream by a predicate for Java 8 workarounds (I recommend this)

Collection<MyCustomRunTimeException> exceptions = Stream.iterate(ex, Throwable::getCause)
    .takeWhile(throwable -> throwable.getCause() != null)
    .filter(element -> element instanceof MyCustomRunTimeException)
    .map(obj -> (MyCustomRunTimeException) obj)
    .collect(Collectors.toList());
like image 32
3 revs Avatar answered Oct 07 '22 19:10

3 revs


You can still create your recursive solution, using java-8. Here is a small helper method that you need:

private static Stream<Throwable> streamIt(Throwable initial) {
    if (initial == null) {
        return Stream.empty();
    }
    return Stream.concat(
        Stream.of(initial),
        Stream.of(initial)
              .map(Throwable::getCause)
              .flatMap(StackWalkerSandbox::streamIt));
}

This will give you a Stream<Throwable>, what you do next with it, it's entirely up to you. For example for your description that you wanted and taken into account the examples from the accepted answer, you could do:

streamIt(exampleFive)
        .filter(MyCustomRunTimeException.class::isInstance)
        .map(MyCustomRunTimeException.class::cast)
        .collect(Collectors.toList())
        .forEach(System.out::println);
like image 1
Eugene Avatar answered Oct 07 '22 20:10

Eugene