Skip to content

Commit

Permalink
Quartz: add a test for programmatic job metadata
Browse files Browse the repository at this point in the history
- this is a follow-up of quarkusio#41370

(cherry picked from commit 985f582)
  • Loading branch information
mkouba authored and gsmet committed Jun 26, 2024
1 parent de50dec commit c3e29ce
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.SchedulerException;

import io.quarkus.arc.Arc;
import io.quarkus.arc.Unremovable;
Expand All @@ -38,6 +41,9 @@ public class ProgrammaticJobsTest {
.withApplicationRoot((jar) -> jar
.addClasses(Jobs.class));

@Inject
org.quartz.Scheduler quartzScheduler;

@Inject
Scheduler scheduler;

Expand Down Expand Up @@ -109,8 +115,9 @@ public void testJobs() throws InterruptedException {
}

@Test
public void testAsyncJob() throws InterruptedException {
JobDefinition asyncJob = scheduler.newJob("fooAsync")
public void testAsyncJob() throws InterruptedException, SchedulerException {
String identity = "fooAsync";
JobDefinition asyncJob = scheduler.newJob(identity)
.setInterval("1s")
.setAsyncTask(ec -> {
assertTrue(Context.isOnEventLoopThread() && VertxContext.isOnDuplicatedContext());
Expand All @@ -125,6 +132,11 @@ public void testAsyncJob() throws InterruptedException {

Trigger trigger = asyncJob.schedule();
assertNotNull(trigger);
// JobKey is always built using the identity and "io.quarkus.scheduler.Scheduler" as the group name
JobDetail jobDetail = quartzScheduler.getJobDetail(new JobKey(identity, Scheduler.class.getName()));
assertNotNull(jobDetail);
// We only store metadata for DB store type
assertNull(jobDetail.getJobDataMap().get("scheduled_metadata"));

assertTrue(ProgrammaticJobsTest.ASYNC_LATCH.await(5, TimeUnit.SECONDS));
assertNotNull(scheduler.unscheduleJob("fooAsync"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1128,12 +1128,12 @@ public void handle(Void event) {

static class QuartzTrigger implements Trigger {

final org.quartz.TriggerKey triggerKey;
final Function<TriggerKey, org.quartz.Trigger> triggerFunction;
final ScheduledInvoker invoker;
final Duration gracePeriod;
final boolean isProgrammatic;
final String methodDescription;
private final org.quartz.TriggerKey triggerKey;
private final Function<TriggerKey, org.quartz.Trigger> triggerFunction;
private final ScheduledInvoker invoker;
private final Duration gracePeriod;
private final boolean isProgrammatic;
private final String methodDescription;

final boolean runBlockingMethodOnQuartzThread;

Expand All @@ -1151,13 +1151,13 @@ static class QuartzTrigger implements Trigger {

@Override
public Instant getNextFireTime() {
Date nextFireTime = getTrigger().getNextFireTime();
Date nextFireTime = trigger().getNextFireTime();
return nextFireTime != null ? nextFireTime.toInstant() : null;
}

@Override
public Instant getPreviousFireTime() {
Date previousFireTime = getTrigger().getPreviousFireTime();
Date previousFireTime = trigger().getPreviousFireTime();
return previousFireTime != null ? previousFireTime.toInstant() : null;
}

Expand All @@ -1173,18 +1173,18 @@ public boolean isOverdue() {

@Override
public String getId() {
return getTrigger().getKey().getName();
}

private org.quartz.Trigger getTrigger() {
return triggerFunction.apply(triggerKey);
return trigger().getKey().getName();
}

@Override
public String getMethodDescription() {
return methodDescription;
}

private org.quartz.Trigger trigger() {
return triggerFunction.apply(triggerKey);
}

}

static class QuartzScheduledExecution implements ScheduledExecution {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;

Expand All @@ -20,6 +22,7 @@
import io.quarkus.scheduler.Scheduler;
import io.quarkus.scheduler.Scheduler.JobDefinition;
import io.quarkus.scheduler.Trigger;
import io.quarkus.scheduler.common.runtime.SyntheticScheduled;
import io.smallrye.mutiny.Uni;

@Path("/scheduler/programmatic")
Expand All @@ -33,7 +36,8 @@ public class ProgrammaticJobResource {

@POST
@Path("register")
public void register() {
@Produces(MediaType.TEXT_PLAIN)
public String register() throws SchedulerException {
JobDefinition syncJob = scheduler.newJob("sync").setInterval("1s");
try {
syncJob.setTask(se -> {
Expand All @@ -53,6 +57,21 @@ public void register() {
} catch (IllegalStateException expected) {
}
syncJob.setTask(SyncJob.class).schedule();
// Verify the serialized metadata
// JobKey is always built using the identity and "io.quarkus.scheduler.Scheduler" as the group name
JobDetail syncJobDetail = scheduler.getScheduler().getJobDetail(new JobKey("sync", Scheduler.class.getName()));
if (syncJobDetail == null) {
return "Syn job detail not found";
}
SyntheticScheduled syncMetadata = SyntheticScheduled
.fromJson(syncJobDetail.getJobDataMap().get("scheduled_metadata").toString());
if (!syncMetadata.every().equals("1s")) {
return "Sync interval not set";
}
if (!SyncJob.class.getName()
.equals(syncJobDetail.getJobDataMap().getOrDefault("execution_metadata_task_class", "").toString())) {
return "execution_metadata_task_class not set";
}

JobDefinition asyncJob = scheduler.newJob("async").setInterval("1s");
try {
Expand All @@ -61,6 +80,23 @@ public void register() {
} catch (IllegalStateException expected) {
}
asyncJob.setAsyncTask(AsyncJob.class).schedule();

// Verify the serialized metadata
// JobKey is always built using the identity and "io.quarkus.scheduler.Scheduler" as the group name
JobDetail asynJobDetail = scheduler.getScheduler().getJobDetail(new JobKey("async", Scheduler.class.getName()));
if (asynJobDetail == null) {
return "Job detail not found";
}
SyntheticScheduled asyncMetadata = SyntheticScheduled
.fromJson(asynJobDetail.getJobDataMap().get("scheduled_metadata").toString());
if (!asyncMetadata.every().equals("1s")) {
return "Interval not set";
}
if (!AsyncJob.class.getName()
.equals(asynJobDetail.getJobDataMap().getOrDefault("execution_metadata_async_task_class", "").toString())) {
return "execution_metadata_async_task_class not set";
}
return "OK";
}

@GET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ public void testFixedInstanceIdGenerator() {

@Test
public void testProgrammaticJobs() {
given().when().post("/scheduler/programmatic/register").then().statusCode(204);
Response response = given().when().post("/scheduler/programmatic/register");
assertEquals(200, response.statusCode());
assertEquals("OK", response.asString());
assertCounter("/scheduler/programmatic/sync", 1, Duration.ofSeconds(3));
assertCounter("/scheduler/programmatic/async", 1, Duration.ofSeconds(3));
}
Expand Down

0 comments on commit c3e29ce

Please sign in to comment.