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

org.rx.core.FluentWait Maven / Gradle / Ivy

package org.rx.core;

import com.google.common.base.Throwables;
import lombok.*;
import org.rx.beans.DateTime;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.function.Predicate;

import static org.rx.core.Contract.TimeoutInfinite;
import static org.rx.core.Contract.require;

public class FluentWait {
    @Data
    @RequiredArgsConstructor
    public static class UntilState {
        private final DateTime endTime;
        private int checkCount;
    }

    private static final long defaultTimeout = 500L;

    public static UntilState StateNull() {
        return new UntilState(DateTime.now());
    }

    public static FluentWait newInstance(long timeoutMillis) {
        return newInstance(timeoutMillis, defaultTimeout);
    }

    public static FluentWait newInstance(long timeoutMillis, long intervalMillis) {
        return new FluentWait().timeout(timeoutMillis).interval(intervalMillis);
    }

    @Getter
    private long timeout = defaultTimeout;
    @Getter
    private long interval = defaultTimeout;
    private String message;
    private List> ignoredExceptions = new ArrayList<>();
    private boolean throwOnFail = true;
    private long retryMills = TimeoutInfinite;
    private boolean retryFirstCall;

    private FluentWait() {
    }

    public FluentWait timeout(long timeoutMillis) {
        require(timeoutMillis, timeoutMillis > TimeoutInfinite);
        this.timeout = timeoutMillis;
        return this;
    }

    public FluentWait interval(long intervalMillis) {
        require(intervalMillis, intervalMillis > TimeoutInfinite);
        this.interval = intervalMillis;
        return this;
    }

    public FluentWait retryMills(long retryMills) {
        require(retryMills, retryMills >= TimeoutInfinite);
        this.retryMills = retryMills;
        return this;
    }

    public FluentWait retryFirstCall(boolean retryFirstCall) {
        this.retryFirstCall = retryFirstCall;
        return this;
    }

    public FluentWait message(String message) {
        this.message = message;
        return this;
    }

    @SafeVarargs
    public final FluentWait ignoredExceptions(Class... exceptions) {
        ignoredExceptions.addAll(Arrays.toList(exceptions));
        return this;
    }

    public FluentWait throwOnFail(boolean throwOnFail) {
        this.throwOnFail = throwOnFail;
        return this;
    }

    public FluentWait sleep() {
        if (interval > 0) {
            Contract.sleep(interval);
        }
        return this;
    }

    public  T until(Function supplier) throws TimeoutException {
        return until(supplier, null);
    }

    //    @SneakyThrows
    public  T until(Function supplier, Predicate retryFunc) throws TimeoutException {
        require(supplier);

        Throwable lastException;
        T lastResult = null;
        UntilState state = new UntilState(DateTime.now().addMilliseconds((int) timeout));
        if (retryFirstCall && retryFunc != null) {
            retryFunc.test(state);
        }

        int retryCount = retryMills == TimeoutInfinite ? TimeoutInfinite : (int) (interval > 0 ? Math.floor((double) retryMills / interval) : timeout);
        do {
            try {
                lastResult = supplier.apply(state);
                if (lastResult != null && (Boolean.class != lastResult.getClass() || Boolean.TRUE.equals(lastResult))) {
                    return lastResult;
                }
                lastException = null;
            } catch (Throwable e) {
                lastException = propagateIfNotIgnored(e);
            } finally {
                state.checkCount++;
            }

            sleep();

            if (retryMills > TimeoutInfinite && (retryCount == 0 || state.checkCount % retryCount == 0)) {
                if (retryFunc != null && !retryFunc.test(state)) {
                    break;
                }
            }
        }
        while (DateTime.now().before(state.endTime));
        if (!throwOnFail) {
            return lastResult;
        }

        String timeoutMessage = String.format("Expected condition failed: %s (tried for %d millisecond(s) with %d milliseconds interval%s)",
                message == null ? "waiting for " + supplier : message,
                timeout, interval, lastException == null ? "" : " with ignoredException " + lastException);
        throw new TimeoutException(timeoutMessage);
    }

    private Throwable propagateIfNotIgnored(Throwable e) {
        Iterator> tor = this.ignoredExceptions.iterator();
        Class ignoredException;
        do {
            if (!tor.hasNext()) {
                Throwables.throwIfUnchecked(e);
                throw new RuntimeException(e);
            }
            ignoredException = tor.next();
        } while (!ignoredException.isInstance(e));
        return e;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy