Skip to content

Commit

Permalink
Fix #16063
Browse files Browse the repository at this point in the history
add Support for Default Value and for Switching Timer Off
  • Loading branch information
renegrob committed Mar 28, 2021
1 parent 1514566 commit c376e82
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 18 deletions.
Expand Up @@ -4,6 +4,7 @@
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.OptionalLong;
import java.util.Properties;

import javax.annotation.PreDestroy;
Expand Down Expand Up @@ -136,6 +137,9 @@ public QuartzScheduler(SchedulerContext context, QuartzSupport quartzSupport, Sc

String cron = SchedulerUtils.lookUpPropertyValue(scheduled.cron());
if (!cron.isEmpty()) {
if (SchedulerUtils.isOff(cron)) {
continue;
}
if (!CronType.QUARTZ.equals(cronType)) {
// Migrate the expression
Cron cronExpr = parser.parse(cron);
Expand All @@ -152,8 +156,12 @@ public QuartzScheduler(SchedulerContext context, QuartzSupport quartzSupport, Sc
}
scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
} else if (!scheduled.every().isEmpty()) {
OptionalLong everyMillis = SchedulerUtils.parseEveryAsMillis(scheduled);
if (!everyMillis.isPresent()) {
continue;
}
scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMilliseconds(SchedulerUtils.parseEveryAsMillis(scheduled))
.withIntervalInMilliseconds(everyMillis.getAsLong())
.repeatForever();
} else {
throw new IllegalArgumentException("Invalid schedule configuration: " + scheduled);
Expand Down
Expand Up @@ -8,6 +8,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
Expand Down Expand Up @@ -82,12 +83,15 @@ public void run() {
int nameSequence = 0;
for (Scheduled scheduled : method.getSchedules()) {
nameSequence++;
SimpleTrigger trigger = createTrigger(method.getInvokerClassName(), parser, scheduled, nameSequence);
ScheduledInvoker invoker = context.createInvoker(method.getInvokerClassName());
if (scheduled.concurrentExecution() == ConcurrentExecution.SKIP) {
invoker = new SkipConcurrentExecutionInvoker(invoker, skippedExecutionEvent);
Optional<SimpleTrigger> trigger = createTrigger(method.getInvokerClassName(), parser, scheduled,
nameSequence);
if (trigger.isPresent()) {
ScheduledInvoker invoker = context.createInvoker(method.getInvokerClassName());
if (scheduled.concurrentExecution() == ConcurrentExecution.SKIP) {
invoker = new SkipConcurrentExecutionInvoker(invoker, skippedExecutionEvent);
}
scheduledTasks.add(new ScheduledTask(trigger.get(), invoker));
}
scheduledTasks.add(new ScheduledTask(trigger, invoker));
}
}
}
Expand Down Expand Up @@ -151,7 +155,7 @@ public boolean isRunning() {
return enabled && running;
}

SimpleTrigger createTrigger(String invokerClass, CronParser parser, Scheduled scheduled, int nameSequence) {
Optional<SimpleTrigger> createTrigger(String invokerClass, CronParser parser, Scheduled scheduled, int nameSequence) {
String id = SchedulerUtils.lookUpPropertyValue(scheduled.identity());
if (id.isEmpty()) {
id = nameSequence + "_" + invokerClass;
Expand All @@ -169,15 +173,22 @@ SimpleTrigger createTrigger(String invokerClass, CronParser parser, Scheduled sc

String cron = SchedulerUtils.lookUpPropertyValue(scheduled.cron());
if (!cron.isEmpty()) {
if (SchedulerUtils.isOff(cron)) {
return Optional.empty();
}
Cron cronExpr;
try {
cronExpr = parser.parse(cron);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Cannot parse cron expression: " + cron, e);
}
return new CronTrigger(id, start, cronExpr);
return Optional.of(new CronTrigger(id, start, cronExpr));
} else if (!scheduled.every().isEmpty()) {
return new IntervalTrigger(id, start, SchedulerUtils.parseEveryAsMillis(scheduled));
final OptionalLong everyMillis = SchedulerUtils.parseEveryAsMillis(scheduled);
if (!everyMillis.isPresent()) {
return Optional.empty();
}
return Optional.of(new IntervalTrigger(id, start, everyMillis.getAsLong()));
} else {
throw new IllegalArgumentException("Invalid schedule configuration: " + scheduled);
}
Expand Down
@@ -1,10 +1,18 @@
package io.quarkus.scheduler.runtime.util;

import static io.smallrye.common.expression.Expression.Flag.LENIENT_SYNTAX;
import static io.smallrye.common.expression.Expression.Flag.NO_TRIM;

import java.time.Duration;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.OptionalLong;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;

import io.quarkus.scheduler.Scheduled;
import io.smallrye.common.expression.Expression;

/**
* Utilities class for scheduler extensions.
Expand All @@ -26,17 +34,28 @@ private SchedulerUtils() {
* @return returns the duration in milliseconds.
*/
public static long parseDelayedAsMillis(Scheduled scheduled) {
return parseDurationAsMillis(scheduled, scheduled.delayed(), DELAYED);
String value = lookUpPropertyValue(scheduled.delayed());
return parseDurationAsMillis(scheduled, value, DELAYED);
}

/**
* Parse the `@Scheduled(every = "")` field into milliseconds.
*
* @param scheduled annotation
* @return returns the duration in milliseconds.
* @return returns the duration in milliseconds or {@link OptionalLong#empty()} if the expression evaluates to "off" or
* "disabled".
*/
public static long parseEveryAsMillis(Scheduled scheduled) {
return parseDurationAsMillis(scheduled, scheduled.every(), EVERY);
public static OptionalLong parseEveryAsMillis(Scheduled scheduled) {
String value = lookUpPropertyValue(scheduled.every());
OptionalLong optionalMillis = OptionalLong.empty();
if (!isOff(value)) {
optionalMillis = OptionalLong.of(parseDurationAsMillis(scheduled, value, EVERY));
}
return optionalMillis;
}

public static boolean isOff(String value) {
return value != null && (value.equalsIgnoreCase("off") || value.equalsIgnoreCase("disabled"));
}

/**
Expand All @@ -47,28 +66,63 @@ public static long parseEveryAsMillis(Scheduled scheduled) {
*/
public static String lookUpPropertyValue(String propertyValue) {
String value = propertyValue.trim();
if (!value.isEmpty() && isConfigValue(value)) {
value = ConfigProviderResolver.instance().getConfig().getValue(getConfigProperty(value), String.class);
if (!value.isEmpty()) {
// for backwards compatibility
if (isSimpleConfigValue(value)) {
value = ConfigProviderResolver.instance().getConfig().getValue(unwrapSimpleConfigProperty(value), String.class);
} else {
value = resolvePropertyExpression(value);
}
}

return value;
}

public static boolean isConfigValue(String val) {
return isSimpleConfigValue(val) || isConfigExpression(val);
}

private static boolean isSimpleConfigValue(String val) {
val = val.trim();
return val.startsWith("{") && val.endsWith("}");
}

private static String getConfigProperty(String val) {
private static String unwrapSimpleConfigProperty(String val) {
return val.substring(1, val.length() - 1);
}

/**
* Adapted from {@link io.smallrye.config.ExpressionConfigSourceInterceptor}
*/
private static String resolvePropertyExpression(String expr) {
final Config config = ConfigProviderResolver.instance().getConfig();
final Expression expression = compileExpression(expr);
final String expanded = expression.evaluate((resolveContext, stringBuilder) -> {
final Optional<String> resolve = config.getOptionalValue(resolveContext.getKey(), String.class);
if (resolve.isPresent()) {
stringBuilder.append(resolve.get());
} else if (resolveContext.hasDefault()) {
resolveContext.expandDefault();
} else {
throw new NoSuchElementException(String.format("Could not expand value %s in property %s",
resolveContext.getKey(), expr));
}
});
return expanded;
}

private static Expression compileExpression(String expr) {
return Expression.compile(expr, LENIENT_SYNTAX, NO_TRIM);
}

private static boolean isConfigExpression(String val) {
return val != null && compileExpression(val).getReferencedStrings().size() > 0;
}

private static long parseDurationAsMillis(Scheduled scheduled, String value, String memberName) {
return Math.abs(parseDuration(scheduled, value, memberName).toMillis());
}

private static Duration parseDuration(Scheduled scheduled, String value, String memberName) {
value = lookUpPropertyValue(value);
if (Character.isDigit(value.charAt(0))) {
value = "PT" + value;
}
Expand Down

0 comments on commit c376e82

Please sign in to comment.