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

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

There is a newer version: 3.0.0
Show newest version
package org.rx.core;

import com.google.common.base.Throwables;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.rx.exception.InvalidException;
import org.rx.util.function.BiFunc;
import org.rx.util.function.PredicateFunc;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;

import static org.rx.core.Constants.TIMEOUT_INFINITE;
import static org.rx.core.Extends.require;

@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class FluentWait {
    @Data
    public static class UntilState {
        final long endTime;
        private int evaluatedCount;
    }

    public static UntilState emptyState() {
        return new UntilState(TIMEOUT_INFINITE);
    }

    public static FluentWait newInstance(long timeoutMillis) {
        return newInstance(timeoutMillis, Constants.DEFAULT_INTERVAL);
    }

    public static FluentWait newInstance(long timeoutMillis, long intervalMillis) {
        require(timeoutMillis, timeoutMillis > TIMEOUT_INFINITE);
        require(intervalMillis, intervalMillis > TIMEOUT_INFINITE);

        return new FluentWait(timeoutMillis, intervalMillis);
    }

    @Getter
    final long timeout;
    @Getter
    final long interval;
    private long retryMillis = TIMEOUT_INFINITE;
    private boolean doRetryFirst;
    private boolean throwOnTimeout = true;
    private String message;
    private List> ignoredExceptions;

    public FluentWait retryEvery(long interval) {
        return retryEvery(interval, false);
    }

    public FluentWait retryEvery(long interval, boolean doRetryFirst) {
        require(interval, interval >= TIMEOUT_INFINITE);
        this.retryMillis = interval;
        this.doRetryFirst = doRetryFirst;
        return this;
    }

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

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

    @SafeVarargs
    public final FluentWait ignoreExceptions(Class... exceptions) {
        if (ignoredExceptions == null) {
            ignoredExceptions = new ArrayList<>();
        }
        ignoredExceptions.addAll(Arrays.toList(exceptions));
        return this;
    }

    public FluentWait sleep() {
        if (interval > 0) {
            try {
                Thread.sleep(interval);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw InvalidException.wrap(e);
            }
        }
        return this;
    }

    public  T until(BiFunc func) throws TimeoutException {
        return until(func, null);
    }

    /**
     * Repeatedly applies this instance's input value to the given function until one of the following
     * occurs:
     * 
    *
  1. the function returns neither null nor false
  2. *
  3. the function throws an unignored exception
  4. *
  5. the timeout expires
  6. *
  7. the current thread is interrupted
  8. *
* * @param isTrue the parameter to pass to the {@link BiFunc} * @param The function's expected return type. * @return The function's return value if the function returned something different * from null or false before the timeout expired. * @throws TimeoutException If the timeout expires. */ public T until(@NonNull BiFunc isTrue, PredicateFunc retryCondition) throws TimeoutException { if (retryCondition != null && retryMillis == TIMEOUT_INFINITE) { log.warn("Not call retryEvery() before until()"); } UntilState state = new UntilState(System.currentTimeMillis() + timeout); int retryCount = TIMEOUT_INFINITE; if (retryCondition != null) { if (doRetryFirst) { try { retryCondition.invoke(state); } catch (Throwable e) { throw InvalidException.sneaky(e); } } if (retryMillis > TIMEOUT_INFINITE) { retryCount = (int) (interval > 0 ? Math.floor((double) retryMillis / interval) : timeout); } } Throwable lastException; T lastResult = null; do { try { lastResult = isTrue.invoke(state); if (lastResult != null && (!(lastResult instanceof Boolean) || Boolean.TRUE.equals(lastResult))) { return lastResult; } lastException = null; } catch (Throwable e) { lastException = propagateIfNotIgnored(e); } finally { state.evaluatedCount++; } sleep(); if (retryCount > TIMEOUT_INFINITE && (retryCount == 0 || state.evaluatedCount % retryCount == 0)) { try { if (!retryCondition.invoke(state)) { break; } } catch (Throwable e) { throw InvalidException.sneaky(e); } } } while (state.endTime > System.currentTimeMillis()); if (!throwOnTimeout) { return lastResult; } String timeoutMessage = String.format("Expected condition failed: %s (tried for %d millisecond(s) with %d milliseconds interval)", message == null ? "waiting for " + isTrue : message, timeout, interval); throw ResetEventWait.newTimeoutException(timeoutMessage, lastException); } private Throwable propagateIfNotIgnored(Throwable e) { if (ignoredExceptions != null) { for (Class ignoredException : ignoredExceptions) { if (ignoredException.isInstance(e)) { return e; } } } Throwables.throwIfUnchecked(e); throw new RuntimeException(e); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy