Migrate scheduled tasks to a new package and architecture

Refactored scheduled tasks and moved them to a dedicated `scheduled` package. Replaced the legacy `ScheduledTaskManager` and `ScheduledTask` definitions with a redesigned architecture to improve maintainability. Updated related classes and configurations to adopt the new structure.
This commit is contained in:
anthorob committed 2026-09-09 15:10:33 -04:00
1 parent 1b981abdf7
commit 1d737a2a4f
9 files changed
+46 -42

No files matched your search

@@ -16,7 +16,7 @@ import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.command.CommandManager;
import me.bobert.netneutrality.db.SessionManager;
import me.bobert.netneutrality.core.tasks.TaskManagerImpl;
import me.bobert.netneutrality.core.thread.ScheduledTaskManager;
import me.bobert.netneutrality.core.scheduled.ScheduledTaskManager;
import me.bobert.netneutrality.core.thread.ThreadManager;
import me.bobert.netneutrality.db.service.GuildServiceProviderImpl;
import me.bobert.netneutrality.music.MusicService;
@@ -125,9 +125,9 @@ public class App {
ExecutorProvider executors = context.register(new ThreadManager());
ChannelMessagePurger.bind(executors);
// Scheduled tasks (priority 55): constructed early so TaskManager can resolve it; cancelled
// early on shutdown, before executors.
context.register(new ScheduledTaskManager(executors));
// Scheduled tasks (priority 55): discovers and schedules all ScheduledTask implementations
// on start; cancelled early on shutdown, before executors.
context.register(new ScheduledTaskManager(executors, context));
// Command registry (priority 30).
CommandRegistry commands = context.register(new CommandManager(context));
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core.tasks;
package me.bobert.netneutrality.core.scheduled;
/*
* Copyright (c) anthorob
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core.thread;
package me.bobert.netneutrality.core.scheduled;
/*
* Copyright (c) anthorob
@@ -6,9 +6,11 @@ package me.bobert.netneutrality.core.thread;
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.bootstrap.ApplicationContext;
import me.bobert.netneutrality.bootstrap.Lifecycle;
import me.bobert.netneutrality.bootstrap.ReflectionHelper;
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.tasks.ScheduledTask;
import me.bobert.netneutrality.core.thread.ExecutorProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -16,18 +18,24 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.function.Consumer;
/**
* Instance-based {@link Lifecycle} component that owns scheduled fixed-delay tasks.
*
* <p>On {@link #start()} it discovers all concrete {@link ScheduledTask} implementations via
* reflection and schedules them automatically.
*/
public class ScheduledTaskManager implements Lifecycle {
private static final Logger logger = LogManager.getLogger(ScheduledTaskManager.class);
private final Map<String, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
private final ScheduledExecutorService executorService;
private final ApplicationContext context;
public ScheduledTaskManager(ExecutorProvider executors) {
public ScheduledTaskManager(ExecutorProvider executors, ApplicationContext context) {
this.executorService = executors.getScheduledExecutor();
this.context = context;
}
@Override
@@ -38,7 +46,7 @@ public class ScheduledTaskManager implements Lifecycle {
@Override
public void start() {
// Tasks are scheduled by TaskManagerImpl during its own start; nothing to eagerly start.
loadScheduledTasks();
}
@Override
@@ -84,4 +92,21 @@ public class ScheduledTaskManager implements Lifecycle {
scheduledTasks.forEach((id, future) -> future.cancel(false));
scheduledTasks.clear();
}
private void loadScheduledTasks() {
logger.info("Loading scheduled tasks...");
Consumer<ScheduledTask> onLoaded = scheduledTask -> {
String taskId = scheduledTask.getId();
if (taskId == null || taskId.trim().isEmpty()) {
logger.error("Cannot load scheduled task {}: task ID is null or empty", scheduledTask.getClass().getName());
return;
}
schedule(scheduledTask);
};
ReflectionHelper.loadTasks(ScheduledTask.class, context, onLoaded);
}
}
@@ -18,7 +18,7 @@ import me.bobert.netneutrality.db.service.TaskService;
/**
* Owns the in-memory task registry, button/interaction dispatch, and persistable-task loading.
*
* <p>As a {@link Lifecycle} component it loads built-in, scheduled and persisted tasks on
* <p>As a {@link Lifecycle} component it loads built-in and persisted tasks on
* {@link #start()}.
*/
public interface TaskManager extends Lifecycle {
@@ -15,7 +15,6 @@ import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
import me.bobert.netneutrality.core.tasks.trait.WithButton;
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
import me.bobert.netneutrality.core.tasks.trait.WithParent;
import me.bobert.netneutrality.core.thread.ScheduledTaskManager;
import me.bobert.netneutrality.db.service.TaskService;
import me.bobert.netneutrality.exceptions.GracefulContextError;
import me.bobert.netneutrality.exceptions.TaskExecutionException;
@@ -77,7 +76,6 @@ public class TaskManagerImpl implements TaskManager {
logger.info("Initializing task manager...");
loadBuiltInTasks();
loadScheduledTasks();
taskService.loadTasks();
}
@@ -272,28 +270,6 @@ public class TaskManagerImpl implements TaskManager {
ReflectionHelper.loadTasks(BuiltInTask.class, context, onLoaded);
}
private void loadScheduledTasks() {
logger.info("Loading scheduled tasks...");
ScheduledTaskManager scheduledTaskManager = context.get(ScheduledTaskManager.class);
if (scheduledTaskManager == null) {
logger.error("ScheduledTaskManager is not available; cannot load scheduled tasks");
return;
}
Consumer<ScheduledTask> onLoaded = scheduledTask -> {
String taskId = scheduledTask.getId();
if (taskId == null || taskId.trim().isEmpty()) {
logger.error("Cannot load scheduled task {}: task ID is null or empty", scheduledTask.getClass().getName());
return;
}
scheduledTaskManager.schedule(scheduledTask);
};
ReflectionHelper.loadTasks(ScheduledTask.class, context, onLoaded);
}
private void deleteInteraction(Task task) {
if (task instanceof WithParent<?> taskWithParent) {
@@ -1,10 +1,15 @@
package me.bobert.netneutrality.core.tasks;
package me.bobert.netneutrality.scheduled.cleanup;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.scheduled.ScheduledTask;
import me.bobert.netneutrality.core.tasks.BuiltInTask;
import me.bobert.netneutrality.core.tasks.PersistableTask;
import me.bobert.netneutrality.core.tasks.Task;
import me.bobert.netneutrality.core.tasks.TaskManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -13,8 +18,6 @@ import java.util.Objects;
/**
* Periodically removes expired in-memory tasks.
*
* <p>Scheduled task migrated to constructor injection of {@link TaskManager}.
*/
public class TaskCleanup implements ScheduledTask {
private static final Logger logger = LogManager.getLogger(TaskCleanup.class);
@@ -1,11 +1,11 @@
package me.bobert.netneutrality.tasks.music;
package me.bobert.netneutrality.scheduled.music;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.tasks.ScheduledTask;
import me.bobert.netneutrality.core.scheduled.ScheduledTask;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.music.TrackScheduler;
import org.apache.logging.log4j.LogManager;
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.tasks.music;
package me.bobert.netneutrality.scheduled.music;
/*
* Copyright (c) anthorob
@@ -6,7 +6,7 @@ package me.bobert.netneutrality.tasks.music;
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.tasks.ScheduledTask;
import me.bobert.netneutrality.core.scheduled.ScheduledTask;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.music.MusicService;
import net.dv8tion.jda.api.entities.Member;