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

com.metaeffekt.mirror.Retry Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2021-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.metaeffekt.mirror;

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

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class Retry {

    private final static Logger LOG = LoggerFactory.getLogger(Retry.class);

    private final RetrySupplier supplier;

    private final List> exceptions = new ArrayList<>();
    private final List> validators = new ArrayList<>();
    private int retryCount = 0;
    private int delay = 0;
    private Consumer onFailure;

    public interface RetrySupplier {
        T get() throws Throwable;
    }

    public interface RetrySupplierNoReturnValue {
        void get() throws Throwable;
    }

    public Retry(RetrySupplier supplier) {
        this.supplier = supplier;
    }

    public Retry(RetrySupplierNoReturnValue supplier) {
        this.supplier = () -> {
            supplier.get();
            return null;
        };
    }

    public Retry onException(Class exceptionClass) {
        this.exceptions.add(exceptionClass);
        return this;
    }

    public Retry retryCount(int retryCount) {
        this.retryCount = retryCount;
        return this;
    }

    public Retry withValidator(Validator validator) {
        this.validators.add(validator);
        return this;
    }

    public Retry onFailure(Consumer onFailure) {
        this.onFailure = onFailure;
        return this;
    }

    public Retry withDelay(int delay) {
        this.delay = delay;
        return this;
    }

    private boolean isThrowableContained(Throwable throwable) {
        for (Class exceptionClass : exceptions) {
            if (exceptionClass.isAssignableFrom(throwable.getClass())) {
                return true;
            }
        }
        return false;
    }

    public T run() {
        Throwable caughtException = null;

        for (int i = 0; i < retryCount; i++) {
            try {
                final T result = supplier.get();
                if (!validators.isEmpty() && validators.stream().noneMatch(validator -> validator.isValid(result))) {
                    throw new RuntimeException("Retry validation failed on attempt [" + (i + 1) + " / " + retryCount + "]");
                }
                return result;
            } catch (Throwable throwable) {
                if (i == retryCount - 1) {
                    LOG.error("failed last attempt [{}] due to invalid run result: {}", retryCount, throwable.getMessage());
                } else {
                    LOG.warn("retrying next attempt [{} / {}] due to invalid run result: {}", i + 2, retryCount, throwable.getMessage());
                }
                if (isThrowableContained(throwable)) {
                    caughtException = throwable;
                } else {
                    if (onFailure != null) {
                        onFailure.accept(throwable);
                    }
                    throw new RuntimeException(throwable);
                }
            }

            if (delay > 0) {
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException e) {
                    throw new RuntimeException("Unable to sleep", e);
                }
            }
        }

        if (onFailure != null) {
            onFailure.accept(caughtException);
        }

        if (caughtException != null) {
            throw new RuntimeException(caughtException);
        }

        throw new RuntimeException("Retry failed due to unknown reason");
    }

    @FunctionalInterface
    public interface Validator {
        boolean isValid(T result);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy