io.jooby.quartz.QuartzApp Maven / Gradle / Ivy
/*
* Jooby https://jooby.io
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
package io.jooby.quartz;
import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.quartz.CalendarIntervalTrigger;
import org.quartz.CronTrigger;
import org.quartz.DailyTimeIntervalTrigger;
import org.quartz.InterruptableJob;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.impl.matchers.GroupMatcher;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.jooby.Context;
import io.jooby.Jooby;
import io.jooby.Route;
import io.jooby.SneakyThrows;
import io.jooby.internal.quartz.JobGenerator;
/**
* REST API over {@link Scheduler} operations.
*
* @author edgar
* @since 2.5.1
*/
public class QuartzApp extends Jooby {
private Scheduler scheduler;
{
get(
"/",
ctx -> {
Scheduler scheduler = getScheduler();
List jobKeys = new ArrayList<>();
for (String group : scheduler.getJobGroupNames()) {
jobKeys.addAll(scheduler.getJobKeys(GroupMatcher.groupEquals(group)));
}
return jobKeys.stream().map(key -> job(ctx, scheduler, key)).collect(Collectors.toList());
});
get(
"/{group}/{name}",
ctx -> {
Scheduler scheduler = getScheduler();
return job(ctx, scheduler, jobKey(ctx));
});
get(
"/{group}/{name}/trigger",
ctx -> {
Scheduler scheduler = getScheduler();
JobKey jobKey = jobKey(ctx);
if (!isJobRunning(scheduler, jobKey)) {
scheduler.triggerJob(jobKey, new JobDataMap(ctx.queryMap()));
}
return toMap(
scheduler.getJobDetail(jobKey), scheduler.getTriggersOfJob(jobKey), zoneId(ctx));
});
get(
"/{group}/{name}/interrupt",
ctx -> {
Scheduler scheduler = getScheduler();
JobKey jobKey = jobKey(ctx);
for (JobExecutionContext jec : scheduler.getCurrentlyExecutingJobs()) {
JobKey currentKey = jec.getJobDetail().getKey();
if (currentKey.equals(jobKey)) {
if (jec.getJobInstance() instanceof InterruptableJob) {
((InterruptableJob) jec.getJobInstance()).interrupt();
}
}
}
return toMap(
scheduler.getJobDetail(jobKey), scheduler.getTriggersOfJob(jobKey), zoneId(ctx));
});
get(
"/{group}/{name}/pause",
ctx -> {
Scheduler scheduler = getScheduler();
JobKey jobKey = jobKey(ctx);
scheduler.pauseJob(jobKey);
return toMap(
scheduler.getJobDetail(jobKey), scheduler.getTriggersOfJob(jobKey), zoneId(ctx));
});
get(
"/{group}/{name}/resume",
ctx -> {
Scheduler scheduler = getScheduler();
JobKey jobKey = jobKey(ctx);
scheduler.resumeJob(jobKey);
return toMap(
scheduler.getJobDetail(jobKey), scheduler.getTriggersOfJob(jobKey), zoneId(ctx));
});
get(
"/{group}/{name}/reschedule",
ctx -> {
Scheduler scheduler = getScheduler();
String value = ctx.query("trigger").value();
JobKey jobKey = jobKey(ctx);
Trigger trigger = JobGenerator.newTrigger(getConfig(), value, jobKey);
scheduler.rescheduleJob(trigger.getKey(), trigger);
return toMap(
scheduler.getJobDetail(jobKey), scheduler.getTriggersOfJob(jobKey), zoneId(ctx));
});
/** Delete a job and their associated trigger(s). */
delete(
"/{group}/{name}",
ctx -> {
var scheduler = getScheduler();
var jobKey = jobKey(ctx);
var deleted = scheduler.deleteJob(jobKey);
return Map.of("key", jobKey.toString(), "deleted", deleted);
});
}
private Map job(Context ctx, Scheduler scheduler, JobKey jobKey) {
try {
Map job =
toMap(scheduler.getJobDetail(jobKey), scheduler.getTriggersOfJob(jobKey), zoneId(ctx));
job.put("actions", actions(ctx, jobKey));
return job;
} catch (SchedulerException x) {
throw SneakyThrows.propagate(x);
}
}
private ZoneId zoneId(Context ctx) {
return ctx.query("zoneId").toOptional(ZoneId.class).orElse(ZoneId.of("UTC"));
}
private JobKey jobKey(Context ctx) {
return JobKey.jobKey(ctx.path("name").value(), ctx.path("group").value());
}
private Map toMap(
JobDetail detail, List extends Trigger> triggers, ZoneId zoneId) throws SchedulerException {
Map json = new LinkedHashMap<>();
json.put("key", detail.getKey().toString());
Optional.ofNullable(detail.getDescription()).ifPresent(value -> json.put("description", value));
json.put("jobDataMap", detail.getJobDataMap());
json.put("stoppable", InterruptableJob.class.isAssignableFrom(detail.getJobClass()));
json.put("concurrentExecutionDisallowed", detail.isConcurrentExectionDisallowed());
json.put("durable", detail.isDurable());
json.put("persistJobDataAfterExecution", detail.isPersistJobDataAfterExecution());
json.put("requestsRecovery", detail.requestsRecovery());
json.put("triggers", toJson(triggers, zoneId));
return json;
}
private Map actions(Context ctx, JobKey key) {
Map actions = new LinkedHashMap<>();
Route route = ctx.getRoute();
String path;
String base;
if (route.getPathKeys().isEmpty()) {
base = route.getPattern();
path = route.getPattern() + "/" + key.getGroup() + "/" + key.getName();
} else {
base = route.getPattern().substring(0, route.getPattern().indexOf('{') - 1);
path = route.reverse(Map.of("group", key.getGroup(), "name", key.getName()));
}
actions.put("base", base);
actions.put("detail", path);
actions.put("trigger", path + "/trigger");
actions.put("interrupt", path + "/interrupt");
actions.put("pause", path + "/pause");
actions.put("resume", path + "/resume");
actions.put("reschedule", path + "/reschedule");
return actions;
}
private List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy