package tc.oc.commons.core.scheduler; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Inject; import java.time.Duration; import tc.oc.commons.core.plugin.PluginScoped; /** * Generic {@link SchedulerBackend} implementation using the JDK {@link ScheduledExecutorService}. */ @PluginScoped public class SchedulerBackendImpl implements SchedulerBackend<SchedulerBackendImpl.BackendTask> { private final ScheduledExecutorService executor; private int lastId; @Inject protected SchedulerBackendImpl(ThreadFactory threadFactory) { this.executor = Executors.newSingleThreadScheduledExecutor(threadFactory); } protected Runnable decorateRunnable(Runnable runnable) { return runnable; } @Override public int taskId(BackendTask task) { return task.id; } @Override public boolean isTaskQueued(BackendTask backendTask) { return backendTask.isQueued(); } @Override public boolean isTaskRunning(BackendTask backendTask) { return backendTask.running.get(); } @Override public BackendTask startTask(Task.Parameters schedule, Runnable runnable) { return new BackendTask(schedule, decorateRunnable(runnable)); } @Override public void cancelTask(BackendTask backendTask) { backendTask.future.cancel(false); } class BackendTask { final int id; final ScheduledFuture<?> future; final Task.Parameters schedule; final Runnable runnable; final AtomicBoolean running = new AtomicBoolean(); BackendTask(Task.Parameters schedule, Runnable runnable) { this.schedule = schedule; this.runnable = runnable; this.id = ++lastId; final Duration delay = schedule.delay(); final Duration interval = schedule.interval(); if(interval == null) { future = executor.schedule( this.runnable, delay == null ? 0 : delay.toNanos(), TimeUnit.NANOSECONDS ); } else { future = executor.scheduleAtFixedRate( this.runnable, delay == null ? 0 : delay.toNanos(), interval == null ? 0 : interval.toNanos(), TimeUnit.NANOSECONDS ); } } boolean isQueued() { return !future.isDone() && !running.get(); } } }