Skip to content
This repository was archived by the owner on Dec 1, 2021. It is now read-only.

Commit d30e152

Browse files
committed
Added quartz scheduler and used this instead of manual scheduled executor service.
1 parent 12445a6 commit d30e152

File tree

6 files changed

+138
-13
lines changed

6 files changed

+138
-13
lines changed

build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,16 @@ dependencies {
2727
implementation 'com.h2database:h2:1.4.200'
2828
implementation 'com.zaxxer:HikariCP:5.0.0'
2929

30+
implementation 'org.quartz-scheduler:quartz:2.3.2'
31+
3032
// Lombok annotations
3133
compileOnly 'org.projectlombok:lombok:1.18.22'
3234
annotationProcessor 'org.projectlombok:lombok:1.18.22'
3335
testCompileOnly 'org.projectlombok:lombok:1.18.22'
3436
testAnnotationProcessor 'org.projectlombok:lombok:1.18.22'
3537

3638
// Logging
37-
implementation 'ch.qos.logback:logback-classic:1.2.6'
39+
implementation 'ch.qos.logback:logback-classic:1.2.7'
3840

3941
// JUnit tests
4042
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'

src/main/java/net/javadiscord/javabot2/Bot.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@
1010
import net.javadiscord.javabot2.command.SlashCommandListener;
1111
import net.javadiscord.javabot2.config.BotConfig;
1212
import net.javadiscord.javabot2.db.DbHelper;
13-
import net.javadiscord.javabot2.systems.moderation.ModerationService;
13+
import net.javadiscord.javabot2.tasks.ScheduledTasks;
1414
import org.javacord.api.DiscordApi;
1515
import org.javacord.api.DiscordApiBuilder;
1616
import org.javacord.api.entity.intent.Intent;
17+
import org.quartz.SchedulerException;
1718

1819
import java.nio.file.Path;
1920
import java.time.ZoneOffset;
2021
import java.util.TimeZone;
2122
import java.util.concurrent.Executors;
2223
import java.util.concurrent.ScheduledExecutorService;
23-
import java.util.concurrent.TimeUnit;
2424

2525
/**
2626
* The main program entry point.
@@ -81,7 +81,13 @@ public static void main(String[] args) {
8181
"commands/moderation.yaml"
8282
);
8383
api.addSlashCommandCreateListener(commandListener);
84-
initScheduledTasks(api);
84+
try {
85+
ScheduledTasks.init(api);
86+
log.info("Initialized scheduled tasks.");
87+
} catch (SchedulerException e) {
88+
log.error("Could not initialize all scheduled tasks.", e);
89+
api.disconnect();
90+
}
8591
}
8692

8793
/**
@@ -106,13 +112,4 @@ private static MongoDatabase initMongoDatabase() {
106112
warnCollection.createIndex(Indexes.descending("createdAt"), new IndexOptions().unique(false));
107113
return db;
108114
}
109-
110-
private static void initScheduledTasks(DiscordApi api) {
111-
// Regularly check for and unmute users whose mutes have expired.
112-
asyncPool.scheduleAtFixedRate(() -> {
113-
for (var server : api.getServers()) {
114-
new ModerationService(api, config.get(server).getModeration()).unmuteExpired();
115-
}
116-
}, 1L, 1L, TimeUnit.MINUTES);
117-
}
118115
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package net.javadiscord.javabot2.tasks;
2+
3+
import net.javadiscord.javabot2.tasks.jobs.DiscordApiJob;
4+
import net.javadiscord.javabot2.tasks.jobs.UnmuteExpiredJob;
5+
import org.javacord.api.DiscordApi;
6+
import org.quartz.*;
7+
import org.quartz.impl.StdSchedulerFactory;
8+
9+
/**
10+
* This class is responsible for setting up all scheduled tasks that the bot
11+
* should run periodically, using the Quartz {@link Scheduler}. To add new tasks
12+
* to the schedule, add them to the {@link ScheduledTasks#scheduleAllTasks(Scheduler, DiscordApi)}
13+
* method.
14+
*/
15+
public class ScheduledTasks {
16+
// Hide the constructor.
17+
private ScheduledTasks() {}
18+
19+
/**
20+
* Initializes all scheduled jobs and starts the scheduler. Also adds a
21+
* shutdown hook that gracefully stops the scheduler when the program ends.
22+
* @param api The Discord API, which may be needed by some jobs.
23+
* @throws SchedulerException If an error occurs while starting the scheduler.
24+
*/
25+
public static void init(DiscordApi api) throws SchedulerException {
26+
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
27+
scheduleAllTasks(scheduler, api);
28+
scheduler.start();
29+
// Add a hook to shut down the scheduler cleanly when the program terminates.
30+
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
31+
try {
32+
scheduler.shutdown();
33+
} catch (SchedulerException e) {
34+
e.printStackTrace();
35+
}
36+
}));
37+
}
38+
39+
/**
40+
* This method is where all tasks are scheduled. <strong>Add scheduled tasks
41+
* to the scheduler using this method!</strong>
42+
* @param scheduler The scheduler to use.
43+
* @param api The Discord API.
44+
* @throws SchedulerException If an error occurs while adding a task.
45+
*/
46+
private static void scheduleAllTasks(Scheduler scheduler, DiscordApi api) throws SchedulerException {
47+
scheduleApiJob(scheduler, api, UnmuteExpiredJob.class, SimpleScheduleBuilder.repeatMinutelyForever());
48+
}
49+
50+
/**
51+
* Convenience method for scheduling an API-dependent job using a single
52+
* trigger that follows a given schedule.
53+
* @param scheduler The scheduler to add the job to.
54+
* @param api The Discord API.
55+
* @param type The type of job to schedule.
56+
* @param scheduleBuilder A schedule builder that the trigger will use.
57+
* @throws SchedulerException If an error occurs while adding the job.
58+
* @see SimpleScheduleBuilder
59+
* @see CronScheduleBuilder
60+
* @see CalendarIntervalScheduleBuilder
61+
*/
62+
private static void scheduleApiJob(
63+
Scheduler scheduler,
64+
DiscordApi api,
65+
Class<? extends DiscordApiJob> type,
66+
ScheduleBuilder<?> scheduleBuilder
67+
) throws SchedulerException {
68+
scheduler.scheduleJob(
69+
DiscordApiJob.build(type, api),
70+
TriggerBuilder.newTrigger().withSchedule(scheduleBuilder).build()
71+
);
72+
}
73+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package net.javadiscord.javabot2.tasks.jobs;
2+
3+
import org.javacord.api.DiscordApi;
4+
import org.quartz.*;
5+
6+
import java.util.Map;
7+
8+
/**
9+
* A type of job which requires a reference to a {@link DiscordApi} to be
10+
* available at execution time. Extend this class if your job needs the api.
11+
*/
12+
public abstract class DiscordApiJob implements Job {
13+
@Override
14+
public void execute(JobExecutionContext context) throws JobExecutionException {
15+
DiscordApi api = (DiscordApi) context.getJobDetail().getJobDataMap().get("discord-api");
16+
execute(context, api);
17+
}
18+
19+
protected abstract void execute(JobExecutionContext context, DiscordApi api) throws JobExecutionException;
20+
21+
/**
22+
* Builder method that produces a {@link JobDetail} for the given job type,
23+
* with job data initialized to include a reference to the given Discord API.
24+
* @param jobType The type of job to create a job detail for.
25+
* @param api The Discord API.
26+
* @return The created job detail.
27+
*/
28+
public static JobDetail build(Class<? extends DiscordApiJob> jobType, DiscordApi api) {
29+
return JobBuilder.newJob(jobType)
30+
.usingJobData(new JobDataMap(Map.of("discord-api", api)))
31+
.build();
32+
}
33+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package net.javadiscord.javabot2.tasks.jobs;
2+
3+
import net.javadiscord.javabot2.Bot;
4+
import net.javadiscord.javabot2.systems.moderation.ModerationService;
5+
import org.javacord.api.DiscordApi;
6+
import org.quartz.JobExecutionContext;
7+
8+
/**
9+
* Job which unmutes users whose mutes have expired.
10+
*/
11+
public class UnmuteExpiredJob extends DiscordApiJob {
12+
@Override
13+
protected void execute(JobExecutionContext context, DiscordApi api) {
14+
for (var server : api.getServers()) {
15+
new ModerationService(api, Bot.config.get(server).getModeration()).unmuteExpired();
16+
}
17+
}
18+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.quartz.threadPool.threadCount = 4
2+
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

0 commit comments

Comments
 (0)