package de.tum.in.www1.jReto.util; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class Timer { public static interface Action { public void run(Timer timer, int executionCount); } public static interface DelayGenerator { public double getDelay(int executionCount); } private static class ConstantDelayGenerator implements DelayGenerator { private final double delay; public ConstantDelayGenerator(double delay) { this.delay = delay; } @Override public double getDelay(int executionCount) { return delay; } } private static class BackOffDelayGenerator implements DelayGenerator { private final double initialDelay; private final double backOffFactor; private final double maximumDelay; public BackOffDelayGenerator(double initialDelay, double backOffFactor, double maximumDelay) { this.initialDelay = initialDelay; this.backOffFactor = backOffFactor; this.maximumDelay = maximumDelay; } @Override public double getDelay(int executionCount) { double delay = this.initialDelay * Math.pow(backOffFactor, executionCount); return Math.min(delay, this.maximumDelay); } } public static Timer delay(final double delay, Executor executor, final Runnable action) { return new Timer(new Timer.Action() { @Override public void run(Timer timer, int executionCount) { action.run(); } }, new ConstantDelayGenerator(delay), 1, executor); } public static Timer repeat(final double interval, Executor executor, Timer.Action action) { return new Timer(action, new ConstantDelayGenerator(interval), executor); } public static Timer repeat(final double interval, int maximumExecutionCount, Executor executor, Timer.Action action) { return new Timer(action, new ConstantDelayGenerator(interval), maximumExecutionCount, executor); } public static Timer repeatWithBackoff(double initialDelay, double backOffFactor, double maximumDelay, Executor executor, Action action) { return new Timer(action, new BackOffDelayGenerator(initialDelay, backOffFactor, maximumDelay), executor); } public static Timer repeatWithBackoff(double initialDelay, double backOffFactor, double maximumDelay, int maximumExecutionCount, Executor executor, Action action) { return new Timer(action, new BackOffDelayGenerator(initialDelay, backOffFactor, maximumDelay), maximumExecutionCount, executor); } public static class BackoffTimerSettings { public final double initialDelay; public final double backoffFactor; public final double maximumDelay; public BackoffTimerSettings(double initialDelay, double backoffFactor, double maximumDelay) { this.initialDelay = initialDelay; this.backoffFactor = backoffFactor; this.maximumDelay = maximumDelay; } } public static Timer repeatWithBackoff(BackoffTimerSettings settings, Executor executor, Action action) { return Timer.repeatWithBackoff(settings.initialDelay, settings.backoffFactor, settings.maximumDelay, executor, action); } public static Timer repeat(DelayGenerator delayGenerator, Executor executor, Action action) { return new Timer(action, delayGenerator, executor); } public static Timer repeat(DelayGenerator delayGenerator, Executor executor, int maximumExecutionCount, Action action) { return new Timer(action, delayGenerator, maximumExecutionCount, executor); } private static ScheduledExecutorService timerExecutor = Executors.newSingleThreadScheduledExecutor(); private final Executor executor; private final Timer.Action action; private final Timer.DelayGenerator delayGenerator; private final int maximumExecutions; private final boolean limitNumberOfExecutions; private int currentExecutionCount = 0; private boolean isDone = false; private Timer(Timer.Action action, Timer.DelayGenerator delayGenerator, boolean limitNumberOfExecutions, int maximumExecutionCount, Executor executor) { this.action = action; this.delayGenerator = delayGenerator; this.maximumExecutions = maximumExecutionCount; this.limitNumberOfExecutions = limitNumberOfExecutions; this.executor = executor; this.startTimer(); } private Timer(Timer.Action action, Timer.DelayGenerator delayGenerator, int maximumExecutionCount, Executor executor) { this(action, delayGenerator, true, maximumExecutionCount, executor); } private Timer(Timer.Action action, Timer.DelayGenerator delayGenerator, Executor executor) { this(action, delayGenerator, false, 0, executor); } public void stop() { this.isDone = true; } public void fire() { if (this.isDone) return; Timer.this.action.run(Timer.this, Timer.this.currentExecutionCount); this.currentExecutionCount++; if (this.limitNumberOfExecutions && this.currentExecutionCount >= this.maximumExecutions) { this.isDone = true; } if (!this.isDone) { this.startTimer(); } } private void startTimer() { long delay = (long)(this.delayGenerator.getDelay(this.currentExecutionCount) * 1000); Timer.timerExecutor.schedule(() -> Timer.this.executor.execute(() -> Timer.this.fire()), delay, TimeUnit.MILLISECONDS); } }