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

com.github.davidmoten.odata.client.Retries Maven / Gradle / Ivy

The newest version!
package com.github.davidmoten.odata.client;

import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.davidmoten.guavamini.Preconditions;

public final class Retries {

    private static final Logger log = LoggerFactory.getLogger(Retries.class);

    public static final Retries NONE = Retries.builder().maxRetries(0).keepGoingIf(t -> false).build();

    private final long maxRetries;
    private final Iterable retryIntervalsMs;
    private final Supplier> keepGoingIf;

    Retries(long maxRetries, Iterable retryIntervalMs,
            Supplier> keepGoingIf) {
        Preconditions.checkArgument(maxRetries >= 0);
        Preconditions.checkNotNull(retryIntervalMs);
        Preconditions.checkNotNull(keepGoingIf);
        this.maxRetries = maxRetries;
        this.retryIntervalsMs = retryIntervalMs;
        this.keepGoingIf = keepGoingIf;
    }

    public void performWithRetries(RunnableThrowing runnable) {
        int attempt = 0;
        Throwable error = null;
        Function keepGoing = keepGoingIf().get();
        Iterator intervalsMs = retryIntervalsMs().iterator();
        while (true) {
            if (attempt > maxRetries()) {
                throw new RetryException("attempts greater than maxRetries", error);
            }
            attempt++;
            try {
                runnable.run();
                break;
            } catch (Throwable e) {
                error = e;
                log.debug(e.getMessage(), e);
                if (!keepGoing.apply(e)) {
                    throw new RetryException("exception not retryable", e);
                }
                if (!intervalsMs.hasNext()) {
                    throw new RetryException("stopping retries because no more intervals specified");
                }
                long waitMs = intervalsMs.next();
                log.debug("sleeping " + waitMs + "ms");
                sleep(waitMs);
            }
        }
    }

    public static Builder builder() {
        return new Builder();
    }

    public long maxRetries() {
        return maxRetries;
    }

    public Iterable retryIntervalsMs() {
        return retryIntervalsMs;
    }

    public Supplier> keepGoingIf() {
        return keepGoingIf;
    }
    
    private static final class ForeverZero implements Iterable {
        @Override
        public Iterator iterator() {
            return new ForeverZeroIterator(); 
        }
    }

    private static final class ForeverZeroIterator implements Iterator {

        @Override
        public boolean hasNext() {
            return true;
        }

        @Override
        public Long next() {
            return 0L;
        }
    }
    
    public static final class Builder {

        private static final Iterable NO_INTERVAL = new ForeverZero();
        
        private long maxRetries = 0;
        private Iterable retryIntervalsMs = NO_INTERVAL;
        private Supplier> keepGoingIf = () -> (t -> !(t instanceof Error));

        Builder() {
        }

        public Builder maxRetries(long maxRetries) {
            this.maxRetries = maxRetries;
            return this;
        }

        public Builder retryIntervalsMs(Iterable retryIntervalsMs) {
            this.retryIntervalsMs = retryIntervalsMs;
            return this;
        }

        public Builder keepGoingIf(Function keepGoingIf) {
            this.keepGoingIf = () -> keepGoingIf;
            return this;
        }

        public Builder keepGoingIf(Supplier> keepGoingIf) {
            this.keepGoingIf = keepGoingIf;
            return this;
        }

        public Builder retryIntervals(Iterable retryIntervals, TimeUnit unit) {
            return retryIntervalsMs(createRetryIntervalMsIterable(retryIntervals, unit));
        }

        private static Iterable createRetryIntervalMsIterable(Iterable retryIntervals, TimeUnit unit) {
            return new Iterable() {

                @Override
                public Iterator iterator() {
                    Iterator it = retryIntervals.iterator();
                    return new Iterator() {

                        @Override
                        public boolean hasNext() {
                            return it.hasNext();
                        }

                        @Override
                        public Long next() {
                            return unit.toMillis(it.next());
                        }
                    };
                }
            };
        }

        public Builder cappedExponentialRetry(long initial, double factor, long cap, TimeUnit unit) {
            Preconditions.checkArgument(initial >= 0);
            Preconditions.checkArgument(factor >= 0);
            Preconditions.checkArgument(cap >= 0);
            Preconditions.checkNotNull(unit);
            return retryIntervalsMs(createCappedExponentialRetryIterable(initial, factor, cap, unit));
        }
        
        private static Iterable createCappedExponentialRetryIterable(long initial, double factor, long cap, TimeUnit unit) {
            return new Iterable() {
                @Override
                public Iterator iterator() {
                    return new Iterator() {

                        long v = initial;

                        @Override
                        public boolean hasNext() {
                            return true;
                        }

                        @Override
                        public Long next() {
                            long w = v;
                            v = Math.round(Math.min(v * factor, cap));
                            return unit.toMillis(w);
                        }
                    };
                }
            };
        }

        public Retries build() {
            return new Retries(maxRetries, retryIntervalsMs, keepGoingIf);
        }
    }

    private static void sleep(long waitMs) {
        try {
            Thread.sleep(waitMs);
        } catch (InterruptedException interruptedException) {
            throw new RetryException("interrupted", interruptedException);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy