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

com.github.siwenyan.common.Retry Maven / Gradle / Ivy

There is a newer version: 1.25
Show newest version
package com.github.siwenyan.common;

import org.apache.log4j.Logger;

public abstract class Retry implements IAction {
    private static final long TIMEOUT_IN_MILLIS_DEFAULT = 60 * 1000;
    private static final long SLEEP_IN_MILLIS_DEFAULT = 1000;
    private static final long BREAK_IN_MILLIS_DEFAULT = 100;
    private static Logger log = Logger.getLogger(Retry.class);

    private final String trace;
    private final long timeoutInMillis;
    private final long sleepInMillis;
    private final long breakInMillis;
    private final IEvaluator untilCondition;
    private final DoubleAction doubleTry;
    private final DoubleAction doubleUntil;
    private final DoubleAction doubleEarlyUntil;

    protected Object outcomeCompleted = null;
    protected Object outcomeTimeout = null;

    private IAction tryOnce = new IAction() {
        @Override
        public boolean execute() throws Exception {
            try {
                tryOnce();
                return true;
            } catch (ImmediateAbortException e) {
                log.error(trace + "Immediate Abort at tryOnce(): " + e.getMessage());
                throw e;
            } catch (Exception e) {
                return false;
            }
        }
    };

    private IAction weakRecover = new IAction() {
        @Override
        public boolean execute() throws Exception {
            try {
                weakRecover(null);
                return true;
            } catch (ImmediateAbortException e) {
                log.error(trace + "Immediate Abort at weakRecover(): " + e.getMessage());
                throw e;
            } catch (Exception e) {
                return false;
            }
        }
    };

    private IAction until = new IAction() {
        @Override
        public boolean execute() throws Exception {
            if (null == untilCondition) {
                return until();
            } else {
                return untilCondition.evaluate() && until();
            }
        }
    };

    private IAction earlyUntil = new IAction() {
        //early until evaluation is possible only when untilCondition is not null

        @Override
        public boolean execute() throws Exception {
            if (null == untilCondition) {
                return false;
            } else {
                return untilCondition.evaluate() && until();
            }
        }
    };

    private IAction untilRecover = new IAction() {
        @Override
        public boolean execute() throws Exception {
            return untilRecover(null);
        }
    };

    public Retry(String trace, long timeoutInMillis, long sleepInMillis, long breakInMillis, IEvaluator untilCondition) {

        this.trace = (null == trace ? "" : trace.trim()) + ">";
        this.timeoutInMillis = timeoutInMillis > 0 ? timeoutInMillis : TIMEOUT_IN_MILLIS_DEFAULT;
        this.sleepInMillis = sleepInMillis > 0 ? sleepInMillis : SLEEP_IN_MILLIS_DEFAULT;
        this.breakInMillis = breakInMillis > 0 ? breakInMillis : BREAK_IN_MILLIS_DEFAULT;

        this.untilCondition = untilCondition;
        this.doubleTry = new DoubleAction(tryOnce, weakRecover, trace, getBreakMillis());
        this.doubleUntil = new DoubleAction(until, untilRecover, trace, getBreakMillis());
        this.doubleEarlyUntil = new DoubleAction(earlyUntil, untilRecover, trace, getBreakMillis());
    }

    public Retry(String trace) {
        this(trace, -1, -1, -1, null);
    }

    public Retry(String trace, IEvaluator untilCondition) {
        this(trace, -1, -1, -1, untilCondition);
    }

    public Retry(String trace, long timeoutInMillis, long sleepInMillis, long breakInMillis) {
        this(trace, timeoutInMillis, sleepInMillis, breakInMillis, null);
    }

    @Override
    public boolean execute() {
        if (doubleEarlyUntil.execute()) {
            return true;
        }
        long t0 = System.currentTimeMillis();
        long t1 = t0 + this.getTimeoutMillis();
        int retries = 0;
        while (System.currentTimeMillis() < t1) {
            log.warn(trace + ": #" + (retries + 1));
            if (doubleTry.execute()) {
                if (doubleUntil.execute()) {
                    return true;
                }
            }
            retries++;
            log.debug(trace + "Wait a while and retry.");
            sleep(this.getSleepMillis());
        }

        // timeout exit
        this.timeout();
        log.warn(trace + "Timeout after retrying " + retries + " time(s) in " + ((System.currentTimeMillis() - t0) / 1000) + " seconds.");
        return false;
    }

    abstract protected void tryOnce() throws Exception;

    public Object getOutcomeCompleted() {
        return this.outcomeCompleted;
    }

    public void setOutcomeCompleted(Object o) {
        this.outcomeCompleted = o;
    }

    public Object getOutcomeTimeout() {
        return this.outcomeTimeout;
    }

    public void setOutcomeTimeout(Object o) {
        this.outcomeTimeout = o;
    }

    protected boolean until() throws Exception {
        // no until check by default
        log.debug(trace + "No implementation for until() method.");
        return true;
    }

    protected void weakRecover(Exception e) throws Exception {
        // do nothing by default
        // to be overridden
        log.debug(trace + "No implementation for weakRecover() method.");
    }

    protected boolean untilRecover(Exception e) throws Exception {
        // do nothing by default
        // to be overridden
        log.debug(trace + "No implementation for untilRecover() method.");
        log.debug(trace + "Return false means no need to check until condition for the second time.");
        return false;
    }

    protected long getBreakMillis() {
        return this.breakInMillis;
    }

    protected long getSleepMillis() {
        return this.sleepInMillis;
    }

    protected long getTimeoutMillis() {
        return this.timeoutInMillis;
    }

    protected void timeout() {
        // do nothing by default
        log.debug(trace + "No implementation for timeout() method.");
    }

    private void sleep(long millis) {
        if (millis <= 0) {
            return;
        }
        try {
            log.debug(trace + "Wait for " + (millis / 1000) + " seconds.");
            Thread.sleep(millis);
        } catch (Exception e) {
            // do nothing
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy