1
2
3
4
5
6 package org.tailormap.api.scheduling;
7
8 import java.lang.invoke.MethodHandles;
9 import java.util.Set;
10 import java.util.UUID;
11 import org.jspecify.annotations.Nullable;
12 import org.quartz.CronScheduleBuilder;
13 import org.quartz.DateBuilder;
14 import org.quartz.JobBuilder;
15 import org.quartz.JobDataMap;
16 import org.quartz.JobDetail;
17 import org.quartz.JobKey;
18 import org.quartz.ObjectAlreadyExistsException;
19 import org.quartz.Scheduler;
20 import org.quartz.SchedulerException;
21 import org.quartz.Trigger;
22 import org.quartz.TriggerBuilder;
23 import org.quartz.impl.matchers.GroupMatcher;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.springframework.beans.factory.annotation.Autowired;
27 import org.springframework.scheduling.quartz.QuartzJobBean;
28 import org.springframework.stereotype.Service;
29
30 @Service
31 public class TaskManagerService {
32 private static final Logger logger =
33 LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
34
35 private final Scheduler scheduler;
36
37 public TaskManagerService(@Autowired Scheduler scheduler) {
38 this.scheduler = scheduler;
39 }
40
41
42
43
44
45
46
47
48
49 public UUID createTask(Class<? extends QuartzJobBean> job, TMJobDataMap jobData) throws SchedulerException {
50 JobDetail jobDetail = JobBuilder.newJob(job)
51 .withIdentity(new JobKey(
52 UUID.randomUUID().toString(), jobData.get(Task.TYPE_KEY).toString()))
53 .withDescription(jobData.getDescription())
54 .usingJobData(new JobDataMap(jobData))
55 .storeDurably(false)
56 .build();
57
58 Trigger trigger = TriggerBuilder.newTrigger()
59 .withIdentity(jobDetail.getKey().getName(), jobDetail.getKey().getGroup())
60 .startNow()
61 .withPriority(jobData.getPriority())
62 .forJob(jobDetail)
63 .build();
64
65 scheduler.scheduleJob(jobDetail, Set.of(trigger), true);
66 return UUID.fromString(jobDetail.getKey().getName());
67 }
68
69
70
71
72
73
74
75
76
77
78 public UUID createTask(Class<? extends QuartzJobBean> job, TMJobDataMap jobData, String cronExpression)
79 throws SchedulerException {
80
81
82 JobDetail jobDetail = JobBuilder.newJob(job)
83 .withIdentity(new JobKey(
84 UUID.randomUUID().toString(), jobData.get(Task.TYPE_KEY).toString()))
85 .withDescription(jobData.getDescription())
86 .usingJobData(new JobDataMap(jobData))
87 .build();
88
89
90 Trigger trigger = TriggerBuilder.newTrigger()
91 .withIdentity(jobDetail.getKey().getName(), jobDetail.getKey().getGroup())
92 .startAt(DateBuilder.futureDate(90, DateBuilder.IntervalUnit.SECOND))
93 .withPriority(jobData.getPriority())
94 .withSchedule(
95 CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionFireAndProceed())
96 .build();
97
98 try {
99 scheduler.scheduleJob(jobDetail, trigger);
100 } catch (ObjectAlreadyExistsException ex) {
101 logger.warn(
102 "Job {} with trigger {} has not bean added to scheduler as it already exists.",
103 jobDetail.getKey(),
104 trigger.getKey());
105 return null;
106 }
107
108 return UUID.fromString(jobDetail.getKey().getName());
109 }
110
111
112
113
114
115
116
117
118 public void updateTask(JobKey jobKey, TMJobDataMap newJobData) throws SchedulerException {
119
120 if (scheduler.checkExists(jobKey)) {
121
122 Trigger oldTrigger = scheduler.getTriggersOfJob(jobKey).getFirst();
123
124 if (scheduler.checkExists(oldTrigger.getKey())) {
125
126 JobDetail jobDetail = scheduler.getJobDetail(jobKey);
127 JobDataMap jobDataMap = jobDetail.getJobDataMap();
128 jobDataMap.putAll(newJobData);
129
130 Trigger newTrigger = TriggerBuilder.newTrigger()
131 .withIdentity(jobKey.getName(), jobKey.getGroup())
132 .startAt(DateBuilder.futureDate(90, DateBuilder.IntervalUnit.SECOND))
133 .withPriority(jobDataMap.getInt(Task.PRIORITY_KEY))
134 .withSchedule(CronScheduleBuilder.cronSchedule(jobDataMap.getString(Task.CRON_EXPRESSION_KEY))
135 .withMisfireHandlingInstructionFireAndProceed())
136 .build();
137
138 scheduler.addJob(jobDetail, true, true);
139 scheduler.rescheduleJob(oldTrigger.getKey(), newTrigger);
140 }
141 }
142 }
143
144
145
146
147
148
149
150
151
152 @Nullable public JobKey getJobKey(TaskType jobType, UUID uuid) throws SchedulerException {
153 logger.debug("Finding job key for task {}:{}", jobType, uuid);
154 return scheduler.getJobKeys(GroupMatcher.groupEquals(jobType.getValue())).stream()
155 .filter(jobkey -> jobkey.getName().equals(uuid.toString()))
156 .findFirst()
157 .orElse(null);
158 }
159
160
161
162
163
164
165
166 public void deleteTasksByGroupName(String groupName) throws SchedulerException {
167 logger.debug("Deleting tasks in group: {}", groupName);
168 Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.groupEquals(groupName));
169 for (JobKey jobKey : jobKeys) {
170 logger.info("Deleting task: {}", jobKey);
171 scheduler.deleteJob(jobKey);
172 }
173 }
174 }