public V call(Callable<V> callable) throws ExecutionException, RetryException {
        long startTime = System.nanoTime();
        for (int attemptNumber = 1; ; attemptNumber++) {
            Attempt<V> attempt;
            try {
                V result = attemptTimeLimiter.call(callable);
                attempt = new ResultAttempt<V>(result, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
            } catch (Throwable t) {
                attempt = new ExceptionAttempt<V>(t, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
            }

            for (RetryListener listener : listeners) {
                listener.onRetry(attempt);
            }

            if (!rejectionPredicate.apply(attempt)) {
                return attempt.get();
            }
            if (stopStrategy.shouldStop(attempt)) {
                throw new RetryException(attemptNumber, attempt);
            } else {
                long sleepTime = waitStrategy.computeSleepTime(attempt);
                try {
                    blockStrategy.block(sleepTime);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RetryException(attemptNumber, attempt);
                }
            }
        }
    }
public V call ( Callable < V > callable ) throws ExecutionException , RetryException { long startTime = System . nanoTime ( ) ; for ( int attemptNumber = 1 ; ; attemptNumber ++ ) { Attempt < V > attempt ; try { V result = attemptTimeLimiter . call ( callable ) ; attempt = new ResultAttempt < V > ( result , attemptNumber , TimeUnit . NANOSECONDS . toMillis ( System . nanoTime ( ) - startTime ) ) ; } catch ( Throwable t ) { attempt = new ExceptionAttempt < V > ( t , attemptNumber , TimeUnit . NANOSECONDS . toMillis ( System . nanoTime ( ) - startTime ) ) ; } for ( RetryListener listener : listeners ) { listener . onRetry ( attempt ) ; } if ( ! rejectionPredicate . apply ( attempt ) ) { return attempt . get ( ) ; } if ( stopStrategy . shouldStop ( attempt ) ) { throw new RetryException ( attemptNumber , attempt ) ; } else { long sleepTime = waitStrategy . computeSleepTime ( attempt ) ; try { blockStrategy . block ( sleepTime ) ; } catch ( InterruptedException e ) { Thread . currentThread ( ) . interrupt ( ) ; throw new RetryException ( attemptNumber , attempt ) ; } } } }
Executes the given callable. If the rejection predicate
accepts the attempt, the stop strategy is used to decide if a new attempt
must be made. Then the wait strategy is used to decide how much time to sleep
and a new attempt is made.

@param callable the callable task to be executed
@return the computed result of the given callable
@throws ExecutionException if the given callable throws an exception, and the
rejection predicate considers the attempt as successful. The original exception
is wrapped into an ExecutionException.
@throws RetryException     if all the attempts failed before the stop strategy decided
to abort, or the thread was interrupted. Note that if the thread is interrupted,
this exception is thrown and the thread's interrupt status is set.