diff --git a/pom.xml b/pom.xml index de99f05e..49214b9c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,18 +4,10 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - - UTF-8 - 4.5.4 - 0.21.0 - 2.15.3 - 5.2.5 - - io.github.sinri Keel - - 3.2.3 + 3.2.4-SNAPSHOT + Keel @@ -44,6 +36,14 @@ + + UTF-8 + 4.5.4 + 0.21.0 + 2.15.3 + 5.2.5 + + io.vertx diff --git a/src/main/java/io/github/sinri/keel/core/KeelCronExpression.java b/src/main/java/io/github/sinri/keel/core/KeelCronExpression.java index 4d9d86cd..a866978c 100644 --- a/src/main/java/io/github/sinri/keel/core/KeelCronExpression.java +++ b/src/main/java/io/github/sinri/keel/core/KeelCronExpression.java @@ -1,5 +1,7 @@ package io.github.sinri.keel.core; + +import javax.annotation.Nonnull; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -13,9 +15,9 @@ public class KeelCronExpression { final Set dayOptions = new HashSet<>(); final Set monthOptions = new HashSet<>(); final Set weekdayOptions = new HashSet<>(); - private final String rawCronExpression; + private final @Nonnull String rawCronExpression; - public KeelCronExpression(String rawCronExpression) { + public KeelCronExpression(@Nonnull String rawCronExpression) { this.rawCronExpression = rawCronExpression; String[] parts = rawCronExpression.trim().split("\\s+"); @@ -36,24 +38,20 @@ public KeelCronExpression(String rawCronExpression) { parseField(weekdayExpression, weekdayOptions, 0, 6); } - public boolean match(Calendar currentCalendar) { - // currentCalendar := Calendar.getInstance(); - int minute = currentCalendar.get(Calendar.MINUTE); - int hour = currentCalendar.get(Calendar.HOUR_OF_DAY); - int day = currentCalendar.get(Calendar.DAY_OF_MONTH); - int month = 1 + currentCalendar.get(Calendar.MONTH);// make JAN 1, ... - int weekday = currentCalendar.get(Calendar.DAY_OF_WEEK) - 1; // make sunday 0, ... - - return minuteOptions.contains(minute) - && hourOptions.contains(hour) - && dayOptions.contains(day) - && monthOptions.contains(month) - && weekdayOptions.contains(weekday); + public boolean match(@Nonnull Calendar currentCalendar) { + ParsedCalenderElements parsedCalenderElements = new ParsedCalenderElements(currentCalendar); + return match(parsedCalenderElements); } - private void parseField(String rawComponent, Set optionSet, int min, int max) { -// System.out.println("parseField: " + rawComponent); + public boolean match(@Nonnull ParsedCalenderElements parsedCalenderElements) { + return minuteOptions.contains(parsedCalenderElements.minute) + && hourOptions.contains(parsedCalenderElements.hour) + && dayOptions.contains(parsedCalenderElements.day) + && monthOptions.contains(parsedCalenderElements.month) + && weekdayOptions.contains(parsedCalenderElements.weekday); + } + private void parseField(@Nonnull String rawComponent, @Nonnull Set optionSet, int min, int max) { if (rawComponent.equals("*")) { for (int i = min; i <= max; i++) { optionSet.add(i); @@ -106,10 +104,35 @@ private void parseField(String rawComponent, Set optionSet, int min, in } } + @Nonnull public String getRawCronExpression() { return rawCronExpression; } + /** + * @since 3.2.4 + */ + public static class ParsedCalenderElements { + public final int minute; + public final int hour; + public final int day; + public final int month; + public final int weekday; + + public ParsedCalenderElements(@Nonnull Calendar currentCalendar) { + minute = currentCalendar.get(Calendar.MINUTE); + hour = currentCalendar.get(Calendar.HOUR_OF_DAY); + day = currentCalendar.get(Calendar.DAY_OF_MONTH); + month = 1 + currentCalendar.get(Calendar.MONTH);// make JAN 1, ... + weekday = currentCalendar.get(Calendar.DAY_OF_WEEK) - 1; // make sunday 0, ... + } + + @Override + public String toString() { + return minute + " " + hour + " " + day + " " + month + " " + weekday; + } + } + @Override public String toString() { return getRawCronExpression(); diff --git a/src/main/java/io/github/sinri/keel/facade/KeelInstance.java b/src/main/java/io/github/sinri/keel/facade/KeelInstance.java index 6a51217e..f89314b1 100644 --- a/src/main/java/io/github/sinri/keel/facade/KeelInstance.java +++ b/src/main/java/io/github/sinri/keel/facade/KeelInstance.java @@ -6,7 +6,10 @@ import io.github.sinri.keel.logger.KeelLogLevel; import io.github.sinri.keel.logger.event.KeelEventLogger; import io.github.sinri.keel.logger.issue.center.KeelIssueRecordCenter; -import io.vertx.core.*; +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; import io.vertx.core.spi.cluster.ClusterManager; import javax.annotation.Nonnull; @@ -130,7 +133,7 @@ public KeelInstance setLogger(@Nonnull KeelEventLogger eventLogger) { return this; } - public Future gracefullyClose(@Nonnull Handler> promiseHandler) { + public Future gracefullyClose(@Nonnull io.vertx.core.Handler> promiseHandler) { Promise promise = Promise.promise(); promiseHandler.handle(promise); return promise.future().compose(v -> getVertx().close()); diff --git a/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundial.java b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundial.java index d814e931..592fc65f 100644 --- a/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundial.java +++ b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundial.java @@ -1,19 +1,22 @@ package io.github.sinri.keel.servant.sundial; +import io.github.sinri.keel.core.KeelCronExpression; +import io.github.sinri.keel.facade.async.KeelAsyncKit; import io.github.sinri.keel.logger.event.KeelEventLogger; import io.github.sinri.keel.logger.issue.center.KeelIssueRecordCenter; import io.github.sinri.keel.verticles.KeelVerticleImplWithEventLogger; import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; import static io.github.sinri.keel.facade.KeelInstance.Keel; /** * @since 3.0.0 + * @since 3.2.4 use verticle to handle the sundial plan executing. */ public abstract class KeelSundial extends KeelVerticleImplWithEventLogger { private final Map planMap = new ConcurrentHashMap<>(); @@ -33,47 +36,55 @@ public void start() throws Exception { this.timerID = Keel.getVertx().setPeriodic(delaySeconds, 60_000L, timerID -> { Calendar calendar = Calendar.getInstance(); handleEveryMinute(calendar); + refreshPlans(); }); } private void handleEveryMinute(Calendar now) { + KeelCronExpression.ParsedCalenderElements parsedCalenderElements = new KeelCronExpression.ParsedCalenderElements(now); planMap.forEach((key, plan) -> { - if (plan.cronExpression().match(now)) { + if (plan.cronExpression().match(parsedCalenderElements)) { + getLogger().debug("Sundial Plan Matched", new JsonObject() + .put("plan_key", plan.key()) + .put("plan_cron", plan.cronExpression()) + .put("now", parsedCalenderElements.toString()) + ); plan.execute(now); + } else { + getLogger().debug("Sundial Plan Not Match", new JsonObject() + .put("plan_key", plan.key()) + .put("plan_cron", plan.cronExpression()) + .put("now", parsedCalenderElements.toString()) + ); } }); - - // refresh plan, pfs {0: not-fetching, more: fetching} - if (planFetchingSemaphore.get() == 0) { - planFetchingSemaphore.incrementAndGet(); - fetchPlans() - .compose(plans -> { - if (plans == null) { - // treat null as NOT MODIFIED - return Future.succeededFuture(); - } - Set toDelete = new HashSet<>(planMap.keySet()); - plans.forEach(plan -> { - toDelete.remove(plan.key()); - planMap.put(plan.key(), plan); - }); - if (!toDelete.isEmpty()) { - toDelete.forEach(planMap::remove); - } - return Future.succeededFuture(); - }) - .eventually(() -> { - planFetchingSemaphore.decrementAndGet(); - return Future.succeededFuture(); - }); - - } - } - @Deprecated(since = "3.0.1", forRemoval = true) - protected Supplier> plansSupplier() { - return null; + /** + * @since 3.2.4 + */ + private void refreshPlans() { + KeelAsyncKit.exclusivelyCall( + "io.github.sinri.keel.servant.sundial.KeelSundial.refreshPlans", + 1000L, + () -> { + return fetchPlans() + .compose(plans -> { + // treat null as NOT MODIFIED + if (plans != null) { + Set toDelete = new HashSet<>(planMap.keySet()); + plans.forEach(plan -> { + toDelete.remove(plan.key()); + planMap.put(plan.key(), plan); + }); + if (!toDelete.isEmpty()) { + toDelete.forEach(planMap::remove); + } + } + return Future.succeededFuture(); + }); + } + ); } /** diff --git a/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialPlan.java b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialPlan.java index 50f52b51..37812b2f 100644 --- a/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialPlan.java +++ b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialPlan.java @@ -1,16 +1,18 @@ package io.github.sinri.keel.servant.sundial; import io.github.sinri.keel.core.KeelCronExpression; +import io.vertx.core.Future; import java.util.Calendar; /** * @since 3.0.0 + * @since 3.2.4 change sync method `execute` to be async. */ public interface KeelSundialPlan { String key(); KeelCronExpression cronExpression(); - void execute(Calendar now); + Future execute(Calendar now); } diff --git a/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialVerticle.java b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialVerticle.java new file mode 100644 index 00000000..c5129046 --- /dev/null +++ b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialVerticle.java @@ -0,0 +1,31 @@ +package io.github.sinri.keel.servant.sundial; + +import io.github.sinri.keel.verticles.KeelVerticleImplPure; +import io.vertx.core.Future; + +import javax.annotation.Nonnull; +import java.util.Calendar; + +/** + * @since 3.2.4 + */ +public class KeelSundialVerticle extends KeelVerticleImplPure { + private final KeelSundialPlan sundialPlan; + private final Calendar now; + + public KeelSundialVerticle(@Nonnull KeelSundialPlan sundialPlan, Calendar now) { + this.sundialPlan = sundialPlan; + this.now = now; + } + + @Override + public void start() throws Exception { + Future.succeededFuture() + .compose(v -> { + return sundialPlan.execute(now); + }) + .onComplete(ar -> { + undeployMe(); + }); + } +}