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

org.catools.common.concurrent.CTimeBoxRunner Maven / Gradle / Ivy

package org.catools.common.concurrent;

import org.catools.common.concurrent.exceptions.CThreadTimeoutException;
import org.catools.common.date.CDate;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * There are many time in automation when you stock on some task
 * due to application response time or dead loop inside automation code.
 * We do want to fix both but we do not want to limit our
 * execution due to such scenarios so we use a {@link CTimeBoxRunner} which
 * is job is to wait for task only in defined time frame and throw exception
 * if the task execution timeout
 *
 * @param  type of result object
 */
public class CTimeBoxRunner implements Runnable {
    private final Supplier job;
    private final int timeoutInSeconds;
    private final boolean throwExceptionIfTimeout;
    private Throwable ex;
    private R r;

    private CTimeBoxRunner(Supplier job, int timeoutInSeconds, boolean throwExceptionIfTimeout) {
        this.job = job;
        this.timeoutInSeconds = timeoutInSeconds;
        this.throwExceptionIfTimeout = throwExceptionIfTimeout;
    }

    /**
     * Perform a task in separate concurrent and return the execution result.
     * If the task not been finished in the defined time box then then
     * return null without throwing any exception
     *
     * @param job              task to be perform
     * @param timeoutInSeconds execution timeout in seconds
     * @param               the type of return objects
     * @return the result of task model
     */
    public static  R get(Supplier job, int timeoutInSeconds) {
        return get(job, timeoutInSeconds, false);
    }

    /**
     * Perform a task in separate concurrent and return the execution result.
     * If the {@code throwExceptionIfTimeout} is set to be FALSE and the task not been finished
     * in the defined time box then then return null without throwing any exception
     * If the {@code throwExceptionIfTimeout} is set to be TRUE and the task not been finished
     * in the defined time box then then throw any exception {@link CThreadTimeoutException}
     *
     * @param job                     task to be perform
     * @param timeout                 execution timeout amount
     * @param unit                    execution timeout time unit
     * @param throwExceptionIfTimeout whether should throw execution on timeout or not
     * @param                      the type of return objects
     * @return the result of task model
     * @throws CThreadTimeoutException throw execution on timeout if throwExceptionIfTimeout parameter set to TRUE
     */
    public static  R get(Supplier job, long timeout, TimeUnit unit, boolean throwExceptionIfTimeout) {
        return new CTimeBoxRunner(job, (int) TimeUnit.SECONDS.convert(timeout, unit), throwExceptionIfTimeout).get();
    }

    /**
     * Perform a task in separate concurrent and return the execution result.
     * If the {@code throwExceptionIfTimeout} is set to be FALSE and the task not been finished
     * in the defined time box then then return null without throwing any exception
     * If the {@code throwExceptionIfTimeout} is set to be TRUE and the task not been finished
     * in the defined time box then then throw any exception {@link CThreadTimeoutException}
     *
     * @param job                     task to be perform
     * @param timeoutInSeconds        execution timeout in seconds
     * @param throwExceptionIfTimeout whether should throw execution on timeout or not
     * @param                      the type of return objects
     * @return the result of task model
     * @throws CThreadTimeoutException throw execution on timeout if throwExceptionIfTimeout parameter set to TRUE
     */
    public static  R get(Supplier job, int timeoutInSeconds, boolean throwExceptionIfTimeout) {
        return new CTimeBoxRunner(job, timeoutInSeconds, throwExceptionIfTimeout).get();
    }

    @Override
    public void run() {
        try {
            r = job.get();
        } catch (Throwable ex) {
            this.ex = ex;
        }
    }

    private R get() {
        Thread thread = CThreadRunner.run(this);
        CDate deadLine = new CDate().addSeconds(timeoutInSeconds);
        boolean timeoutFlag = false;
        // A little ugly code for sake of debugging and branch readability
        while (true) {
            if (r != null) {
                break;
            }

            if (!thread.isAlive()) {
                break;
            }

            if (deadLine.before(new CDate())) {
                timeoutFlag = true;
                break;
            }
        }

        if (thread.isAlive()) {
            thread.interrupt();
        }

        if (timeoutFlag && throwExceptionIfTimeout) {
            throw new CThreadTimeoutException("Job execution takes more time than expected");
        }

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

        return r;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy