Skip to content

Commit

Permalink
Quartz + Scheduler: Allow to use config in Identity field. Fixes #14967
Browse files Browse the repository at this point in the history
  • Loading branch information
Sgitario committed Feb 11, 2021
1 parent 62eba17 commit cebbf9b
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 17 deletions.
9 changes: 9 additions & 0 deletions docs/src/main/asciidoc/scheduler-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ Sometimes a possibility to specify an explicit id may come in handy.
void myMethod() { }
----

If a value starts with `{` and ends with `}` then the scheduler attempts to find a corresponding config property and use the configured value instead.

.Interval Config Property Example
[source,java]
----
@Scheduled(identity = "{myMethod.identity.expr}")
void myMethod() { }
----

=== Delayed Execution

`@Scheduled` provides two ways to delay the time a trigger should start firing at.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.quarkus.quartz.test;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.scheduler.Scheduled;
import io.quarkus.test.QuarkusUnitTest;

public class DuplicateIdentityExpressionTest {

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest()
.setExpectedException(IllegalStateException.class)
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(DuplicateIdentityExpressionTest.InvalidBean.class)
.addAsResource(new StringAsset("my.identity=my_name"),
"application.properties"));

@Test
public void test() {
}

static class InvalidBean {

@Scheduled(every = "1s", identity = "{my.identity}")
@Scheduled(every = "1s", identity = "my_name")
void wrong() {
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.quarkus.quartz.test;

import java.util.NoSuchElementException;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.scheduler.Scheduled;
import io.quarkus.test.QuarkusUnitTest;

public class MissingConfigIdentityExpressionTest {

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest()
.setExpectedException(NoSuchElementException.class)
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(MissingConfigIdentityExpressionTest.InvalidBean.class));

@Test
public void test() {
}

static class InvalidBean {

@Scheduled(every = "1s", identity = "{my.identity}")
void wrong() {
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.quartz.test;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.scheduler.Scheduled;
import io.quarkus.test.QuarkusUnitTest;

public class SimpleIdentityTest {

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Jobs.class)
.addAsResource(new StringAsset("jobs.identity=every_1s_another_name"),
"application.properties"));

@Test
public void testJobsWithIdentity() throws InterruptedException {
// Only assert that the scheduled method is working fine
assertTrue(Jobs.LATCH.await(5, TimeUnit.SECONDS));
}

public static class Jobs {

static final CountDownLatch LATCH = new CountDownLatch(2);

@Scheduled(every = "1s", identity = "every_1s_name")
@Scheduled(every = "1s", identity = "{jobs.identity}")
void ping() {
LATCH.countDown();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public QuartzScheduler(SchedulerContext context, QuartzSupport quartzSupport, Co
int nameSequence = 0;

for (Scheduled scheduled : method.getSchedules()) {
String identity = scheduled.identity().trim();
String identity = SimpleScheduler.lookUpPropertyValue(scheduled.identity());
if (identity.isEmpty()) {
identity = ++nameSequence + "_" + method.getInvokerClassName();
}
Expand All @@ -136,11 +136,8 @@ public QuartzScheduler(SchedulerContext context, QuartzSupport quartzSupport, Co
.requestRecovery();
ScheduleBuilder<?> scheduleBuilder;

String cron = scheduled.cron().trim();
String cron = SimpleScheduler.lookUpPropertyValue(scheduled.cron());
if (!cron.isEmpty()) {
if (SchedulerContext.isConfigValue(cron)) {
cron = config.getValue(SchedulerContext.getConfigProperty(cron), String.class);
}
if (!CronType.QUARTZ.equals(cronType)) {
// Migrate the expression
Cron cronExpr = parser.parse(cron);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ private Throwable validateScheduled(CronParser parser, AnnotationInstance schedu

AnnotationValue identityValue = schedule.value("identity");
if (identityValue != null) {
String identity = identityValue.asString().trim();
String identity = SimpleScheduler.lookUpPropertyValue(identityValue.asString());
AnnotationInstance previousInstanceWithSameIdentity = encounteredIdentities.get(identity);
if (previousInstanceWithSameIdentity != null) {
String message = String.format("The identity: \"%s\" on: %s is not unique and it has already bean used by : %s",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.quarkus.scheduler.test;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.scheduler.Scheduled;
import io.quarkus.test.QuarkusUnitTest;

public class DuplicateIdentityExpressionTest {

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest()
.setExpectedException(IllegalStateException.class)
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(DuplicateIdentityExpressionTest.InvalidBean.class)
.addAsResource(new StringAsset("my.identity=my_name"),
"application.properties"));

@Test
public void test() {
}

static class InvalidBean {

@Scheduled(every = "1s", identity = "{my.identity}")
@Scheduled(every = "1s", identity = "my_name")
void wrong() {
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.quarkus.scheduler.test;

import java.util.NoSuchElementException;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.scheduler.Scheduled;
import io.quarkus.test.QuarkusUnitTest;

public class MissingConfigIdentityExpressionTest {

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest()
.setExpectedException(NoSuchElementException.class)
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(MissingConfigIdentityExpressionTest.InvalidBean.class));

@Test
public void test() {
}

static class InvalidBean {

@Scheduled(every = "1s", identity = "{my.identity}")
void wrong() {
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.inject.Inject;

Expand Down Expand Up @@ -33,15 +34,19 @@ public class PausedSchedulerTest {
public void testSchedulerPauseMethod() throws InterruptedException {
scheduler.pause();
assertFalse(scheduler.isRunning());
Jobs.IS_WATCHING.set(true);
assertFalse(Jobs.LATCH.await(3, TimeUnit.SECONDS));
}

static class Jobs {
static final AtomicBoolean IS_WATCHING = new AtomicBoolean(false);
static final CountDownLatch LATCH = new CountDownLatch(2);

@Scheduled(every = "1s")
void countDownSecond() {
LATCH.countDown();
if (IS_WATCHING.get()) {
LATCH.countDown();
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.scheduler.test;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.scheduler.Scheduled;
import io.quarkus.test.QuarkusUnitTest;

public class SimpleIdentityTest {

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Jobs.class)
.addAsResource(new StringAsset("jobs.identity=every_1s_another_name"),
"application.properties"));

@Test
public void testJobsWithIdentity() throws InterruptedException {
// Only assert that the scheduled method is working fine
assertTrue(Jobs.LATCH.await(5, TimeUnit.SECONDS));
}

public static class Jobs {

static final CountDownLatch LATCH = new CountDownLatch(2);

@Scheduled(every = "1s", identity = "every_1s_name")
@Scheduled(every = "1s", identity = "{jobs.identity}")
void ping() {
LATCH.countDown();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
/**
* Optionally defines a unique identifier for this job.
* <p>
* If the value starts with "&#123;" and ends with "&#125;" the scheduler attempts to find a corresponding config property
* and use the configured value instead: {@code &#64;Scheduled(identity = "{myservice.check.identity.expr}")}.
*
* <p>
* If the value is not given, Quarkus will generate a unique id.
* <p>
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public boolean isRunning() {
}

SimpleTrigger createTrigger(String invokerClass, CronParser parser, Scheduled scheduled, int nameSequence, Config config) {
String id = scheduled.identity().trim();
String id = lookUpPropertyValue(scheduled.identity());
if (id.isEmpty()) {
id = nameSequence + "_" + invokerClass;
}
Expand All @@ -169,11 +169,8 @@ SimpleTrigger createTrigger(String invokerClass, CronParser parser, Scheduled sc
start = start.toInstant().plusMillis(millisToAdd).atZone(start.getZone());
}

String cron = scheduled.cron().trim();
String cron = lookUpPropertyValue(scheduled.cron());
if (!cron.isEmpty()) {
if (SchedulerContext.isConfigValue(cron)) {
cron = config.getValue(SchedulerContext.getConfigProperty(cron), String.class);
}
Cron cronExpr;
try {
cronExpr = parser.parse(cron);
Expand All @@ -190,11 +187,7 @@ SimpleTrigger createTrigger(String invokerClass, CronParser parser, Scheduled sc

// Keep it public so that we can reuse the logic in the quartz extension
public static Duration parseDuration(Scheduled scheduled, String value, String memberName) {
value = value.trim();
if (SchedulerContext.isConfigValue(value)) {
value = ConfigProviderResolver.instance().getConfig().getValue(SchedulerContext.getConfigProperty(value),
String.class);
}
value = lookUpPropertyValue(value);
if (Character.isDigit(value.charAt(0))) {
value = "PT" + value;
}
Expand All @@ -206,6 +199,17 @@ public static Duration parseDuration(Scheduled scheduled, String value, String m
}
}

// Keep it public so that we can reuse the logic in the quartz extension
public static String lookUpPropertyValue(String propertyValue) {
String value = propertyValue.trim();
if (!value.isEmpty() && SchedulerContext.isConfigValue(value)) {
value = ConfigProviderResolver.instance().getConfig().getValue(SchedulerContext.getConfigProperty(value),
String.class);
}

return value;
}

static class ScheduledTask {

final SimpleTrigger trigger;
Expand Down

0 comments on commit cebbf9b

Please sign in to comment.