salex89
9/12/2016 - 3:21 PM

The poller executes an operation a predetermined number of times with pauses between each execution. Success of the operation is determined

The poller executes an operation a predetermined number of times with pauses between each execution. Success of the operation is determined using a supplied predicate. Three callbacks exist: onSuccess, onFailure and onFinish (which is called in any case). onFailure and onFinish can handle based on the last obtained value (if any) sent to the predicate.

import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Created by Aleksandar Stojadinovic on 12/09/16.
 */
public class RetryWithCountdownPoller<T> {

    private Supplier<T> pollMethod = null;
    private Predicate<T> pollResultPredicate = null;
    private AtomicInteger retries;
    private Duration interval;
    private T lastResult;
    private Optional<Consumer<T>> successConsumer;
    private Optional<Consumer<T>> failureConsumer;
    private Optional<Consumer<T>> finishConsumer;

    public RetryWithCountdownPoller() {
        successConsumer = Optional.empty();
        failureConsumer = Optional.empty();
        finishConsumer = Optional.empty();
    }

    public RetryWithCountdownPoller<T> poll(Duration interval, int retries) {
        this.interval = interval;
        this.retries = new AtomicInteger(retries);
        return this;
    }

    public RetryWithCountdownPoller<T> method(Supplier<T> supplier) {
        this.pollMethod = supplier;
        return this;
    }

    public RetryWithCountdownPoller<T> until(Predicate<T> predicate) {
        pollResultPredicate = predicate;
        return this;
    }

    public RetryWithCountdownPoller<T> onFailure(Consumer<T> consumer) {
        this.failureConsumer = Optional.of(consumer);
        return this;
    }

    public RetryWithCountdownPoller<T> onSuccess(Consumer<T> consumer) {
        this.successConsumer = Optional.of(consumer);
        return this;
    }

    public RetryWithCountdownPoller<T> onFinish(Consumer<T> consumer) {
        this.finishConsumer = Optional.of(consumer);
        return this;
    }

    public void execute() {
        boolean pollSucceeded = false;
        while (!pollSucceeded && retries.decrementAndGet() > 0) {
            lastResult = pollMethod.get();
            pollSucceeded = pollResultPredicate.test(lastResult);
            if (pollSucceeded) {
                successConsumer.ifPresent(c -> c.accept(lastResult));
                finishConsumer.ifPresent(c -> c.accept(lastResult));
            }
            if (!pollSucceeded && retries.intValue() > 1) {
                sleep(interval);
            }
        }
        if (!pollSucceeded) {
            failureConsumer.ifPresent(c -> c.accept(lastResult));
            finishConsumer.ifPresent(c -> c.accept(lastResult));
        }
    }

    private void sleep(Duration interval) {
        try {
            Thread.sleep(interval.toMillis());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}