package io.fathom.cloud.tasks; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fathomdb.TimeSpan; import com.google.inject.Injector; @Singleton public class SimpleSchedulerService implements TaskScheduler { private static final Logger log = LoggerFactory.getLogger(SimpleSchedulerService.class); private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); private final ExecutorService threadPool = Executors.newCachedThreadPool(); @Inject Injector injector; @Override public void schedule(final Runnable runnable, TimeSpan delay) { schedule(runnable, delay.getTotalMilliseconds(), TimeUnit.MILLISECONDS); } @Override public ScheduledFuture<?> schedule(final Runnable command, long delay, TimeUnit timeUnit) { ScheduledFuture<?> future = scheduler.schedule(new Runnable() { @Override public void run() { try { threadPool.execute(command); } catch (Exception e) { log.warn("Error executing job on thread pool", e); } } }, delay, timeUnit); return future; } class RepeatingTask implements SchedulerTask { final Runnable runAndReschedule; boolean cancelled; public RepeatingTask(final Runnable command, long initialDelay, final long delay, final TimeUnit timeUnit) { runAndReschedule = new Runnable() { @Override public void run() { try { command.run(); } catch (Exception e) { log.warn("Error executing job on thread pool", e); } synchronized (this) { if (cancelled) { return; } } try { schedule(this, delay, timeUnit); } catch (Throwable t) { log.error("Error rescheduling fetcher", t); } } }; } @Override public void cancel(boolean mayInterruptIfRunning) { synchronized (this) { cancelled = true; } } } @Override public SchedulerTask scheduleWithFixedDelay(final Runnable command, long initialDelay, final long delay, final TimeUnit timeUnit) { RepeatingTask task = new RepeatingTask(command, initialDelay, delay, timeUnit); schedule(task.runAndReschedule, initialDelay, timeUnit); return task; } @Override public void schedule(Class<? extends ScheduledTask> clazz) { ScheduledTask instance = injector.getInstance(clazz); instance.schedule(this); } @Override public Executor getExecutor() { return threadPool; } @Override public void execute(final Callable<?> job) { // TODO: Need to implement real job queuing/retry final Runnable runJob = new Runnable() { @Override public void run() { try { job.call(); return; } catch (Exception e) { log.error("Error running job", e); } log.warn("Job re-scheduling is primitive"); try { schedule(new Runnable() { @Override public void run() { execute(job); } }, TimeSpan.FIVE_SECONDS); } catch (Exception e) { log.error("Error rescheduling job", e); } } }; getExecutor().execute(runJob); } }