Skip to content

Commit 1b7c88e

Browse files
authored
Merge pull request #38021 from mkouba/issue-37805
Scheduler: Quartz - support JobDefinition API if DB type store is used
2 parents 4aa6e27 + 0c126be commit 1b7c88e

File tree

10 files changed

+761
-90
lines changed

10 files changed

+761
-90
lines changed

docs/src/main/asciidoc/scheduler-reference.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ class MyJobs {
406406

407407
NOTE: By default, the scheduler is not started unless a `@Scheduled` business method is found. You may need to force the start of the scheduler for "pure" programmatic scheduling via `quarkus.scheduler.start-mode=forced`.
408408

409-
NOTE: If the xref:quartz.adoc[Quartz extension] is present then only the RAM store type is supported, i.e. `quarkus.quartz.store-type=ram` must be set. The Quartz API can be also used to schedule a job programmatically.
409+
NOTE: If the xref:quartz.adoc[Quartz extension] is present and the DB store type is used then it's not possible to pass a task instance to the job definition and a task class must be used instead. The Quartz API can be also used to schedule a job programmatically.
410410

411411
== Scheduled Methods and Testing
412412

extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/programmatic/ProgrammaticJobsTest.java

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,24 @@
88

99
import java.util.concurrent.CountDownLatch;
1010
import java.util.concurrent.TimeUnit;
11+
import java.util.concurrent.atomic.AtomicInteger;
12+
import java.util.function.Consumer;
13+
import java.util.function.Function;
1114

1215
import jakarta.enterprise.context.RequestScoped;
1316
import jakarta.inject.Inject;
17+
import jakarta.inject.Singleton;
1418

1519
import org.junit.jupiter.api.Test;
1620
import org.junit.jupiter.api.extension.RegisterExtension;
1721

1822
import io.quarkus.arc.Arc;
23+
import io.quarkus.arc.Unremovable;
1924
import io.quarkus.scheduler.Scheduled;
25+
import io.quarkus.scheduler.ScheduledExecution;
2026
import io.quarkus.scheduler.Scheduler;
2127
import io.quarkus.scheduler.Scheduler.JobDefinition;
28+
import io.quarkus.scheduler.Trigger;
2229
import io.quarkus.test.QuarkusUnitTest;
2330
import io.smallrye.common.vertx.VertxContext;
2431
import io.smallrye.mutiny.Uni;
@@ -38,10 +45,24 @@ public class ProgrammaticJobsTest {
3845
MyService myService;
3946

4047
static final CountDownLatch SYNC_LATCH = new CountDownLatch(1);
48+
static final CountDownLatch SYNC_CLASS_LATCH = new CountDownLatch(1);
4149
static final CountDownLatch ASYNC_LATCH = new CountDownLatch(1);
50+
static final AtomicInteger SKIPPED_EXECUTIONS = new AtomicInteger();
51+
static final CountDownLatch ASYNC_CLASS_LATCH = new CountDownLatch(1);
4252

4353
@Test
4454
public void testJobs() throws InterruptedException {
55+
scheduler.newJob("alwaysSkip1")
56+
.setInterval("1s")
57+
.setSkipPredicate(ex -> true)
58+
.setTask(ex -> SKIPPED_EXECUTIONS.incrementAndGet())
59+
.schedule();
60+
scheduler.newJob("alwaysSkip2")
61+
.setInterval("1s")
62+
.setTask(ex -> SKIPPED_EXECUTIONS.incrementAndGet())
63+
.setSkipPredicate(AlwaysSkipPredicate.class)
64+
.schedule();
65+
4566
Scheduler.JobDefinition job1 = scheduler.newJob("foo")
4667
.setInterval("1s")
4768
.setTask(ec -> {
@@ -58,7 +79,8 @@ public void testJobs() throws InterruptedException {
5879
job2.setTask(ec -> {
5980
});
6081

61-
job1.schedule();
82+
Trigger trigger1 = job1.schedule();
83+
assertNotNull(trigger1);
6284
assertTrue(ProgrammaticJobsTest.SYNC_LATCH.await(5, TimeUnit.SECONDS));
6385

6486
assertEquals("Cannot modify a job that was already scheduled",
@@ -79,6 +101,10 @@ public void testJobs() throws InterruptedException {
79101
assertNull(scheduler.unscheduleJob("nonexisting"));
80102

81103
assertNotNull(scheduler.unscheduleJob("foo"));
104+
assertNotNull(scheduler.unscheduleJob("alwaysSkip1"));
105+
assertNotNull(scheduler.unscheduleJob("alwaysSkip2"));
106+
assertEquals(0, SKIPPED_EXECUTIONS.get());
107+
// Jobs#dummy()
82108
assertEquals(1, scheduler.getScheduledJobs().size());
83109
}
84110

@@ -97,12 +123,33 @@ public void testAsyncJob() throws InterruptedException {
97123
assertThrows(IllegalStateException.class, () -> asyncJob.setTask(ec -> {
98124
})).getMessage());
99125

100-
asyncJob.schedule();
126+
Trigger trigger = asyncJob.schedule();
127+
assertNotNull(trigger);
101128

102129
assertTrue(ProgrammaticJobsTest.ASYNC_LATCH.await(5, TimeUnit.SECONDS));
103130
assertNotNull(scheduler.unscheduleJob("fooAsync"));
104131
}
105132

133+
@Test
134+
public void testClassJobs() throws InterruptedException {
135+
scheduler.newJob("fooClass")
136+
.setInterval("1s")
137+
.setTask(JobClassTask.class)
138+
.schedule();
139+
assertTrue(ProgrammaticJobsTest.SYNC_CLASS_LATCH.await(5, TimeUnit.SECONDS));
140+
assertNotNull(scheduler.unscheduleJob("fooClass"));
141+
}
142+
143+
@Test
144+
public void testClassAsyncJobs() throws InterruptedException {
145+
scheduler.newJob("fooAsyncClass")
146+
.setInterval("1s")
147+
.setAsyncTask(JobClassAsyncTask.class)
148+
.schedule();
149+
assertTrue(ProgrammaticJobsTest.ASYNC_CLASS_LATCH.await(5, TimeUnit.SECONDS));
150+
assertNotNull(scheduler.unscheduleJob("fooAsyncClass"));
151+
}
152+
106153
static class Jobs {
107154

108155
@Scheduled(identity = "bar", every = "60m")
@@ -119,4 +166,45 @@ void countDown(CountDownLatch latch) {
119166

120167
}
121168

169+
public static class AlwaysSkipPredicate implements Scheduled.SkipPredicate {
170+
171+
@Override
172+
public boolean test(ScheduledExecution execution) {
173+
return true;
174+
175+
}
176+
}
177+
178+
@Unremovable
179+
@Singleton
180+
public static class JobClassTask implements Consumer<ScheduledExecution> {
181+
182+
@Inject
183+
MyService myService;
184+
185+
@Override
186+
public void accept(ScheduledExecution se) {
187+
assertTrue(Arc.container().requestContext().isActive());
188+
myService.countDown(SYNC_CLASS_LATCH);
189+
}
190+
191+
}
192+
193+
@Unremovable
194+
@Singleton
195+
public static class JobClassAsyncTask implements Function<ScheduledExecution, Uni<Void>> {
196+
197+
@Inject
198+
MyService myService;
199+
200+
@Override
201+
public Uni<Void> apply(ScheduledExecution se) {
202+
assertTrue(Context.isOnEventLoopThread() && VertxContext.isOnDuplicatedContext());
203+
assertTrue(Arc.container().requestContext().isActive());
204+
myService.countDown(ASYNC_CLASS_LATCH);
205+
return Uni.createFrom().voidItem();
206+
}
207+
208+
}
209+
122210
}

0 commit comments

Comments
 (0)