package org.zalando.riptide.exceptions;
import javax.net.ssl.SSLHandshakeException;
import java.io.InterruptedIOException;
import java.net.SocketException;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.function.Predicate;
public interface ExceptionClassifier {
/**
* Classifies the given {@link Throwable throwable} into temporary and permanent exceptions.
*
* @param throwable the throwable
* @return the given throwable if it's considered permanent, ora {@link TemporaryException} with the given
* throwable as its cause, if it's considered temporary
*/
Throwable classify(final Throwable throwable);
/**
* Provides a function that classifies its argument using {@link #classify(Throwable)} and throws it. This function
* is supposed to be used in conjunction with {@link CompletableFuture#exceptionally(Function)}.
*
* @see CompletableFuture#exceptionally(Function)
* @param throwable the throwable
* @param <T> generic return type
* @return never, always throws
*/
<T> T classifyExceptionally(final Throwable throwable);
static ExceptionClassifier createDefault() {
return create(InterruptedIOException.class::isInstance,
SocketException.class::isInstance,
throwable -> throwable instanceof SSLHandshakeException
&& "Remote host closed connection during handshake".equals(throwable.getMessage()));
}
@SafeVarargs
static ExceptionClassifier create(final Predicate<Throwable>... predicates) {
return create(Arrays.asList(predicates));
}
static ExceptionClassifier create(final Collection<Predicate<Throwable>> predicates) {
final Predicate<Throwable> isTemporary = predicates.stream().reduce(Predicate::or).orElse(throwable -> false);
return new DefaultExceptionClassifier(isTemporary);
}
}