package org.zalando.riptide.exceptions; import com.google.common.base.Throwables; import lombok.SneakyThrows; import java.util.concurrent.CompletionException; import java.util.function.Predicate; final class DefaultExceptionClassifier implements ExceptionClassifier { private final Predicate<Throwable> isTemporary; public DefaultExceptionClassifier(final Predicate<Throwable> isTemporary) { this.isTemporary = isTemporary; } @Override public Throwable classify(final Throwable throwable) { final Throwable root = Throwables.getRootCause(throwable); if (isTemporary.test(root)) { return new TemporaryException(throwable); } return throwable; } @Override @SneakyThrows public <T> T classifyExceptionally(final Throwable throwable) { throw classify(skipCompletionException(throwable)); } /** * @see <a href="http://stackoverflow.com/questions/27430255/surprising-behavior-of-java-8-completablefuture-exceptionally-method"/> * @param throwable the throwable * @return the cause of the given throwable if it's a {@link CompletionException} */ private Throwable skipCompletionException(final Throwable throwable) { return throwable instanceof CompletionException ? throwable.getCause() : throwable; } }