package im.tox.upsourcebot.client.tasks;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
/**
* Class for retrying actions with different retry strategies
*/
public class RetryingCallable<E extends Exception, V> implements RecoveringCallable<E, V> {
private static final Logger LOGGER = LoggerFactory.getLogger(RetryingCallable.class);
private static final int SLOT_TIME = 5000;
private static final int CEILING = 16;
private RecoveringCallable<E, V> callable;
private BackoffStrategy backoffStrategy;
public RetryingCallable(RecoveringCallable<E, V> callable, BackoffStrategy backoffStrategy) {
this.callable = callable;
this.backoffStrategy = backoffStrategy;
}
/**
* Static helper method to create a RetryingCallable without a lot of Generic parameters
*/
public static <E extends Exception, V> RetryingCallable<E, V> of(
RecoveringCallable<E, V> recoveringCallable) {
return new RetryingCallable<>(recoveringCallable, getDefaultBackoffStrategy());
}
/**
* Try to execute the callable.
*
* @throws InterruptedException if the containing thread is interrupted
* @throws E if the execution failed after the strategy depleted its retries
*/
@Nullable
@Override
public V call() throws E, InterruptedException {
Exception previousException = null;
for (; backoffStrategy.retryAgain(); backoffStrategy.tryComplete()) {
try {
return callable.call();
} catch (InterruptedException e) {
throw e;
} catch (Exception e) {
LOGGER
.error("Execution of task failed, retrying in " + backoffStrategy.waitTime() + "ms", e);
previousException = e;
Thread.sleep(backoffStrategy.waitTime());
}
}
if (previousException instanceof RuntimeException) {
throw (RuntimeException) previousException;
} else {
throw (E) previousException;
}
}
private static BackoffStrategy getDefaultBackoffStrategy() {
return new ExponentialBackoffStrategy(SLOT_TIME, CEILING);
}
}