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 lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.rx.exception.InvalidException;
import org.rx.util.function.BiAction;
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.*;

@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class FluentWait implements WaitHandle {
    public static FluentWait polling(long timeoutMillis) {
        return polling(timeoutMillis, Constants.DEFAULT_INTERVAL);
    }

    public static FluentWait polling(long timeoutMillis, long intervalMillis) {
        return polling(timeoutMillis, intervalMillis, null);
    }

    public static  FluentWait polling(long timeoutMillis, long intervalMillis, BiFunc resultFunc) {
        require(timeoutMillis, timeoutMillis > TIMEOUT_INFINITE);
        require(intervalMillis, intervalMillis > TIMEOUT_INFINITE);

        FluentWait wait = new FluentWait(timeoutMillis, intervalMillis);
        wait.resultFunc = (BiFunc) resultFunc;
        return wait;
    }

    final long timeout;
    final long interval;
    private BiFunc resultFunc;
    private List> ignoredExceptions;
    private String message;
    private boolean initialDelay;
    private long retryMillis = TIMEOUT_INFINITE;
    private BiAction retryFunc;
    private boolean retryOnStart;
    @Getter
    private int evaluatedCount;
    volatile boolean doBreak;

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

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

    public synchronized FluentWait withInitialDelay() {
        this.initialDelay = true;
        return this;
    }

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

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

    private Throwable propagateIfNotIgnored(Throwable e) {
        if (ignoredExceptions != null) {
            for (Class ignoredException : ignoredExceptions) {
                if (ignoredException.isInstance(e)) {
                    return e;
                }
            }
        }
        throw InvalidException.sneaky(e);
    }

    public boolean awaitTrue(PredicateFunc isTrue) {
        try {
            return ifNull(await(w -> isTrue.invoke(w) ? Boolean.TRUE : null), Boolean.FALSE);
        } catch (TimeoutException e) {
            //ignore
        }
        return false;
    }

    public  T await() throws TimeoutException {
        return (T) await(resultFunc);
    }

    /**
     * Repeatedly applies this instance's input value to the given function until one of the following
     * occurs:
     * 
    *
  1. the function returns neither null
  2. *
  3. the function throws an unignored exception
  4. *
  5. the timeout expires
  6. *
  7. the current thread is interrupted
  8. *
* * @param resultFunc 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 before the timeout expired. * @throws TimeoutException If the timeout expires. */ public synchronized T await(@NonNull BiFunc resultFunc) throws TimeoutException { // if (deadline != 0) { // throw new InvalidException("Not support await nested"); // } doBreak = false; long deadline = System.nanoTime() + timeout * Constants.NANO_TO_MILLIS; try { int retryCount = TIMEOUT_INFINITE; if (retryFunc != null) { if (retryOnStart) { retryFunc.accept(this); } if (retryMillis > TIMEOUT_INFINITE) { retryCount = interval > 0 ? (int) Math.floor((double) retryMillis / interval) : 0; } } boolean doRetry = retryCount > TIMEOUT_INFINITE; Throwable cause; T result = null; if (initialDelay) { sleep(interval); } do { try { if ((result = resultFunc.invoke(this)) != null) { return result; } cause = null; } catch (Throwable e) { cause = propagateIfNotIgnored(e); } finally { evaluatedCount++; } if (doRetry && (retryCount == 0 || evaluatedCount % retryCount == 0)) { retryFunc.accept(this); } if (doBreak) { return result; } sleep(interval); } while (System.nanoTime() < deadline); String timeoutMessage = String.format("Expected condition failed: %s (tried for %d millisecond(s) with %d milliseconds interval)", message == null ? "waiting for " + resultFunc : message, timeout, interval); throw WaitHandle.newTimeoutException(timeoutMessage, cause); } finally { evaluatedCount = 0; } } @Override public boolean await(long timeoutMillis) { try { polling(timeoutMillis, interval, resultFunc).await(); return true; } catch (TimeoutException e) { return false; } } @Override public void signalAll() { doBreak = true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy