package io.cattle.platform.task.impl; import io.cattle.platform.archaius.util.ArchaiusUtil; import io.cattle.platform.deferred.util.DeferredUtils; import io.cattle.platform.eventing.EventService; import io.cattle.platform.framework.event.ExecuteTask; import io.cattle.platform.task.Task; import io.cattle.platform.task.TaskManager; import io.cattle.platform.task.TaskOptions; import io.cattle.platform.task.dao.TaskDao; import io.cattle.platform.util.type.InitializationTask; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.apache.cloudstack.managed.context.NoExceptionRunnable; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.netflix.config.DynamicLongProperty; import com.netflix.config.DynamicStringProperty; public class TaskManagerImpl implements TaskManager, InitializationTask, Runnable { private static final DynamicLongProperty DELAY_SECONDS = ArchaiusUtil.getLong("task.initial.delay.seconds"); private static final String SCHEDULE_FORMAT = "task.%s.schedule"; private static final Logger log = LoggerFactory.getLogger(TaskManagerImpl.class); ScheduledExecutorService executorService; List<Task> tasks; EventService eventService; boolean running = false; Map<String, ScheduledFuture<?>> futures = new ConcurrentHashMap<String, ScheduledFuture<?>>(); Map<String, Runnable> runnables = new ConcurrentHashMap<String, Runnable>(); Map<String, Task> taskMap = new HashMap<String, Task>(); TaskDao taskDao; @Override public void execute(String name) { DeferredUtils.deferPublish(eventService, new ExecuteTask(name)); } @Override public Runnable getRunnable(String name) { return runnables.get(name); } @Override public boolean shouldLock(String name) { Task task = taskMap.get(name); if (task instanceof TaskOptions) { return ((TaskOptions) task).isShouldLock(); } return true; } @Override public void start() { scheduleAll(true); } @Override public void run() { scheduleAll(false); } protected void scheduleAll(boolean initial) { for (Task task : tasks) { schedule(initial, task); } } protected void schedule(boolean initial, final Task task) { final String name = task.getName(); ScheduledFuture<?> future = futures.get(name); if (future != null) { future.cancel(false); } DynamicStringProperty prop = ArchaiusUtil.getString(String.format(SCHEDULE_FORMAT, name)); if (initial) { prop.addCallback(this); } try { Runnable runnable = new NoExceptionRunnable() { @Override protected void doRun() throws Exception { try { task.run(); } catch (Throwable t) { log.error("Task [{}] failed", name, t); } } }; taskDao.register(name); runnables.put(name, runnable); taskMap.put(name, task); if (!StringUtils.isBlank(prop.get())) { long delay = (long) (Float.parseFloat(prop.get()) * 1000); if (delay == 0) { log.info("Disabling task [{}]", name); return; } log.info("Scheduling task [{}] for every [{}] seconds", name, delay); future = executorService.scheduleWithFixedDelay(runnable, Math.min(DELAY_SECONDS.get() * 1000, delay), delay, TimeUnit.MILLISECONDS); futures.put(name, future); } } catch (NumberFormatException nfe) { log.error("Failed to parse [{}] for task [{}]", prop.get(), name, nfe); } } public void stop() { for (ScheduledFuture<?> future : futures.values()) { future.cancel(false); } } public ScheduledExecutorService getExecutorService() { return executorService; } @Inject public void setExecutorService(ScheduledExecutorService executorService) { this.executorService = executorService; } public EventService getEventService() { return eventService; } @Inject public void setEventService(EventService eventService) { this.eventService = eventService; } public TaskDao getTaskDao() { return taskDao; } @Inject public void setTaskDao(TaskDao taskDao) { this.taskDao = taskDao; } public List<Task> getTasks() { return tasks; } @Inject public void setTasks(List<Task> tasks) { this.tasks = tasks; } }