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

org.rnorth.ducttape.unreliables.Unreliables Maven / Gradle / Ivy

Go to download

General purpose resilience utilities for Java 8 (circuit breakers, timeouts, rate limiters, and handlers for unreliable or inconsistent results)

The newest version!
package org.rnorth.ducttape.unreliables;

import org.jetbrains.annotations.NotNull;
import org.rnorth.ducttape.timeouts.Timeouts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.rnorth.ducttape.Preconditions.check;

/**
 * Utilities to support automatic retry of things that may fail.
 */
public abstract class Unreliables {

    private static final Logger LOGGER = LoggerFactory.getLogger(Unreliables.class);

    /**
     * Call a supplier repeatedly until it returns a result. If an exception is thrown, the call
     * will be retried repeatedly until the timeout is hit.
     *
     * @param timeout  how long to wait
     * @param timeUnit time unit for time interval
     * @param lambda   supplier lambda expression (may throw checked exceptions)
     * @param       return type of the supplier
     * @return the result of the successful lambda expression call
     */
    public static  T retryUntilSuccess(final int timeout, @NotNull final TimeUnit timeUnit, @NotNull final Callable lambda) {

        check("timeout must be greater than zero", timeout > 0);

        final int[] attempt = {0};
        final Exception[] lastException = {null};

        final AtomicBoolean doContinue = new AtomicBoolean(true);
        try {
            return Timeouts.getWithTimeout(timeout, timeUnit, () -> {
                while (doContinue.get()) {
                    try {
                        return lambda.call();
                    } catch (Exception e) {
                        // Failed
                        LOGGER.trace("Retrying lambda call on attempt {}", attempt[0]++);
                        lastException[0] = e;
                    }
                }
                return null;
            });
        } catch (org.rnorth.ducttape.TimeoutException e) {
            if (lastException[0] != null) {
                throw new org.rnorth.ducttape.TimeoutException("Timeout waiting for result with exception", lastException[0]);
            } else {
                throw new org.rnorth.ducttape.TimeoutException(e);
            }
        } finally {
            doContinue.set(false);
        }
    }

    /**
     * Call a supplier repeatedly until it returns a result. If an exception is thrown, the call
     * will be retried repeatedly until the retry limit is hit.
     *
     * @param tryLimit how many times to try calling the supplier
     * @param lambda   supplier lambda expression (may throw checked exceptions)
     * @param       return type of the supplier
     * @return the result of the successful lambda expression call
     */
    public static  T retryUntilSuccess(final int tryLimit, @NotNull final Callable lambda) {

        check("tryLimit must be greater than zero", tryLimit > 0);

        int attempt = 0;
        Exception lastException = null;

        while (attempt < tryLimit) {
            try {
                return lambda.call();
            } catch (Exception e) {
                lastException = e;
                attempt++;
            }
        }

        throw new org.rnorth.ducttape.RetryCountExceededException("Retry limit hit with exception", lastException);
    }

    /**
     * Call a callable repeatedly until it returns true. If an exception is thrown, the call
     * will be retried repeatedly until the timeout is hit.
     *
     * @param timeout  how long to wait
     * @param timeUnit time unit for time interval
     * @param lambda   supplier lambda expression
     */
    public static void retryUntilTrue(final int timeout, @NotNull final TimeUnit timeUnit, @NotNull final Callable lambda) {
        retryUntilSuccess(timeout, timeUnit, () -> {
            if (!lambda.call()) {
                throw new RuntimeException("Not ready yet");
            } else {
                return null;
            }
        });
    }

    /**
     * Call a callable repeatedly until it returns true. If an exception is thrown, the call
     * will be retried repeatedly until the timeout is hit.
     *
     * @param tryLimit how many times to try calling the supplier
     * @param lambda   supplier lambda expression
     */
    public static void retryUntilTrue(final int tryLimit, @NotNull final Callable lambda) {
        retryUntilSuccess(tryLimit, () -> {
            if (!lambda.call()) {
                throw new RuntimeException("Not ready yet");
            } else {
                return null;
            }
        });
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy