package jalview.ws2.actions; import java.io.IOException; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.WeakHashMap; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletionException; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import jalview.ws2.actions.api.TaskI; public class PollingTaskExecutor { private static final Map executorPool = Collections.synchronizedMap(new WeakHashMap<>()); public static PollingTaskExecutor fromPool(ScheduledExecutorService executor) { return executorPool.computeIfAbsent(executor, PollingTaskExecutor::new); } private final ScheduledExecutorService executor; public PollingTaskExecutor(ScheduledExecutorService executor) { this.executor = executor; } public Future submit(TaskI task) { Objects.requireNonNull(task); return executor.scheduleWithFixedDelay( new TaskRunnable(task), 0, 2, TimeUnit.SECONDS); } private static class TaskRunnable implements Runnable { private final TaskI task; private static final int STAGE_INIT = 0; private static final int STAGE_POLLING = 2; private static final int STAGE_FINISHED = 3; private static final int STAGE_STOPPED = 4; private int stage = STAGE_INIT; private static final int MAX_POLL_RETRY = 5; private int pollRetryCount = 0; private TaskRunnable(TaskI task) { this.task = task; } @Override public void run() { if (task.getStatus().isDone()) { stage = STAGE_STOPPED; } if (stage == STAGE_INIT) { try { task.init(); stage = STAGE_POLLING; } catch (Exception e) { stage = STAGE_STOPPED; throw new CompletionException(e); } } try { if (stage == STAGE_POLLING && task.poll()) { stage = STAGE_FINISHED; } if (stage == STAGE_FINISHED) { task.complete(); stage = STAGE_STOPPED; } } catch (Exception e) { if (++pollRetryCount > MAX_POLL_RETRY || e instanceof RuntimeException) { task.cancel(); stage = STAGE_STOPPED; throw new CompletionException(e); } } if (stage == STAGE_STOPPED) { throw new CancellationException(); } } } }