All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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