public <T, X extends Throwable> T run(CheckedSupplier<? extends T, X> operation) throws X { for (int attempt = 0;; attempt++) { try { return operation.get(); } catch (Throwable e) { if (exceptionMatcher.test(e)) { if (attempt == attempts - 1) { final String faultMessage = String.format("Fault (attempt #%,d of %,d): aborting", attempt + 1, attempts); errorHandler.onException(faultMessage, e); throw e; } else { final String retryMessage = String.format("Fault (attempt #%,d of %,d): retrying in %,d ms", attempt + 1, attempts, backoffMillis); faultHandler.onException(retryMessage, e); if (! Threads.sleep(backoffMillis)) { final String interruptMessage = String.format("Fault (attempt #%,d of %,d): aborting due to interrupt", attempt + 1, attempts); errorHandler.onException(interruptMessage, e); throw e; } } } else { throw e; } } } } }