
org.jsoftware.utils.retriable.Retriable Maven / Gradle / Ivy
The newest version!
package org.jsoftware.utils.retriable;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Retry any task you want
* @author m-szalik
*/
public class Retriable {
private static final Retriable INSTANCE = new Retriable();
private static final RetriableDelayFunction DEFAULT_FUNCT = RetriableDelayFunction.expFunction();
private final ScheduledExecutorService executorService;
private Retriable() {
executorService = Executors.newScheduledThreadPool(1, new RetriableThreadFactory());
}
private Future doTryInternal(final Callable callable, final int limit, final RetriableDelayFunction delayFunction) {
final RetriableFuture future = new RetriableFuture<>();
executorService.execute(new RetriableRunnable(callable, limit, delayFunction, future));
return future;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
executorService.shutdownNow();
}
/**
* Retry task
* @param callable task to retry
* @param limit retry limit
* @param delayFunction time delay between retries
* @param result type
* @return Future for this task
*/
public static Future doTry(final Callable callable, final int limit, final RetriableDelayFunction delayFunction) {
return INSTANCE.doTryInternal(callable, limit, delayFunction);
}
/**
* Retry task with default delay between retries
* @param callable task to retry
* @param limit retry limit
* @param result type
* @return Future for this task
*/
public static Future doTry(final Callable callable, final int limit) {
return INSTANCE.doTryInternal(callable, limit, DEFAULT_FUNCT);
}
/**
* Retry task
* @param runnable task to retry
* @param limit retry limit
* @param delayFunction time delay between retries
* @return Future for this task
*/
public static Future doTry(final Runnable runnable, final int limit, final RetriableDelayFunction delayFunction) {
return INSTANCE.doTryInternal(new Callable() {
@Override
public Void call() throws Exception {
runnable.run();
return null;
}
}, limit, delayFunction);
}
/**
* Retry task with default delay between retries
* @param runnable task to retry
* @param limit retry limit
* @return Future for this task
*/
public static Future doTry(final Runnable runnable, final int limit) {
return INSTANCE.doTryInternal(new Callable() {
@Override
public Void call() throws Exception {
runnable.run();
return null;
}
}, limit, DEFAULT_FUNCT);
}
class RetriableRunnable implements Runnable {
private final Callable callable;
private final int limit;
private final RetriableDelayFunction delayFunction;
private final RetriableFuture future;
RetriableRunnable(Callable callable, int limit, RetriableDelayFunction delayFunction, RetriableFuture future) {
this.callable = callable;
this.limit = limit;
this.delayFunction = delayFunction;
this.future = future;
}
@Override
public void run() {
if (! future.isCancelled()) {
int tryNo = future.tryCount() + 1;
try {
T result = callable.call();
future.done(result);
} catch (Exception ex) {
future.setLastError(ex);
if (tryNo < limit) {
long delay = delayFunction.retryWait(tryNo);
if (delay >= 0) {
executorService.schedule(this, delay, TimeUnit.MILLISECONDS);
} else {
future.finish();
}
} else {
future.finish();
}
} finally {
future.setTryCount(tryNo);
}
} else {
future.finish();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy