From 303cdddd929c73da9ca339b33925009f99b51d85 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Tue, 25 Jul 2023 14:10:24 +1000 Subject: [PATCH] Remove Old Dev UI: Scheduler Signed-off-by: Phillip Kruger --- .../deployment/SchedulerProcessor.java | 20 -- .../resources/dev-templates/embedded.html | 3 - .../resources/dev-templates/schedules.html | 112 ------------ .../dev-templates/tags/configVal.html | 1 - .../dev-templates/tags/scheduleInfo.html | 28 --- .../test/devconsole/BodyHandlerBean.java | 34 ---- ...onsoleRunScheduledTaskBodyHandlerTest.java | 38 ---- .../DevConsoleRunScheduledTaskTest.java | 35 ---- .../test/devconsole/NeverRunTask.java | 42 ----- .../SchedulerDevConsoleRecorder.java | 173 ------------------ .../DevConsoleSchedulerSmokeTest.java | 41 ----- 11 files changed, 527 deletions(-) delete mode 100644 extensions/scheduler/deployment/src/main/resources/dev-templates/embedded.html delete mode 100644 extensions/scheduler/deployment/src/main/resources/dev-templates/schedules.html delete mode 100644 extensions/scheduler/deployment/src/main/resources/dev-templates/tags/configVal.html delete mode 100644 extensions/scheduler/deployment/src/main/resources/dev-templates/tags/scheduleInfo.html delete mode 100644 extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/BodyHandlerBean.java delete mode 100644 extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/DevConsoleRunScheduledTaskBodyHandlerTest.java delete mode 100644 extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/DevConsoleRunScheduledTaskTest.java delete mode 100644 extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/NeverRunTask.java delete mode 100644 extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/devconsole/SchedulerDevConsoleRecorder.java delete mode 100644 integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleSchedulerSmokeTest.java diff --git a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java index e9a89c1a0ff30..068f4b21748dc 100644 --- a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java +++ b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java @@ -1,7 +1,6 @@ package io.quarkus.scheduler.deployment; import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; -import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; import static org.jboss.jandex.AnnotationTarget.Kind.METHOD; import static org.jboss.jandex.AnnotationValue.createArrayValue; import static org.jboss.jandex.AnnotationValue.createBooleanValue; @@ -54,7 +53,6 @@ import io.quarkus.arc.processor.BeanInfo; import io.quarkus.arc.processor.BuiltinScope; import io.quarkus.arc.processor.DotNames; -import io.quarkus.arc.runtime.BeanLookupSupplier; import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; @@ -68,9 +66,6 @@ import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; -import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; -import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem; -import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem; import io.quarkus.gizmo.CatchBlockCreator; import io.quarkus.gizmo.ClassCreator; import io.quarkus.gizmo.ClassOutput; @@ -83,7 +78,6 @@ import io.quarkus.runtime.util.HashUtil; import io.quarkus.scheduler.Scheduled; import io.quarkus.scheduler.ScheduledExecution; -import io.quarkus.scheduler.Scheduler; import io.quarkus.scheduler.common.runtime.DefaultInvoker; import io.quarkus.scheduler.common.runtime.MutableScheduledMethod; import io.quarkus.scheduler.common.runtime.SchedulerContext; @@ -91,7 +85,6 @@ import io.quarkus.scheduler.runtime.SchedulerConfig; import io.quarkus.scheduler.runtime.SchedulerRecorder; import io.quarkus.scheduler.runtime.SimpleScheduler; -import io.quarkus.scheduler.runtime.devconsole.SchedulerDevConsoleRecorder; public class SchedulerProcessor { @@ -325,19 +318,6 @@ public String apply(String name) { return new FeatureBuildItem(Feature.SCHEDULER); } - @BuildStep - @Record(value = STATIC_INIT, optional = true) - public DevConsoleRouteBuildItem devConsole(BuildProducer infos, - SchedulerDevConsoleRecorder recorder, CurateOutcomeBuildItem curateOutcomeBuildItem) { - infos.produce(new DevConsoleRuntimeTemplateInfoBuildItem("schedulerContext", - new BeanLookupSupplier(SchedulerContext.class), this.getClass(), curateOutcomeBuildItem)); - infos.produce(new DevConsoleRuntimeTemplateInfoBuildItem("scheduler", - new BeanLookupSupplier(Scheduler.class), this.getClass(), curateOutcomeBuildItem)); - infos.produce(new DevConsoleRuntimeTemplateInfoBuildItem("configLookup", - recorder.getConfigLookup(), this.getClass(), curateOutcomeBuildItem)); - return new DevConsoleRouteBuildItem("schedules", "POST", recorder.invokeHandler()); - } - @BuildStep public void metrics(SchedulerConfig config, Optional metricsCapability, diff --git a/extensions/scheduler/deployment/src/main/resources/dev-templates/embedded.html b/extensions/scheduler/deployment/src/main/resources/dev-templates/embedded.html deleted file mode 100644 index 67aec7ee1b7f6..0000000000000 --- a/extensions/scheduler/deployment/src/main/resources/dev-templates/embedded.html +++ /dev/null @@ -1,3 +0,0 @@ - - - Scheduled Methods {info:schedulerContext.scheduledMethods.size()} diff --git a/extensions/scheduler/deployment/src/main/resources/dev-templates/schedules.html b/extensions/scheduler/deployment/src/main/resources/dev-templates/schedules.html deleted file mode 100644 index b05ef8aacd3be..0000000000000 --- a/extensions/scheduler/deployment/src/main/resources/dev-templates/schedules.html +++ /dev/null @@ -1,112 +0,0 @@ -{#include main fluid=true} - {#title}Scheduled Methods{/title} - {#style} - span.app-class { - cursor:pointer; - color:blue; - text-decoration:underline; - } - - .formInputButton:hover { - color: #3366ac !important; - cursor: pointer; - } - - #filterInputGroup { - padding-bottom: 10px; - } - {/style} - {#script} - $(document).ready(function(){ - $("#filterInput").on("keyup", function() { - var value = $(this).val().toLowerCase(); - $(".schedulerTable tr").filter(function() { - $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1); - }); - }); - - if (!ideKnown()) { - return; - } - $(".class-candidate").each(function() { - var className = $(this).text(); - if (appClassLocation(className)) { - $(this).addClass("app-class"); - } - }); - - $(".app-class").on("click", function() { - openInIDE($(this).text()); - }); - }); - - function clearFilterInput(){ - $("#filterInput").val(""); - $(".schedulerTable tr").filter(function() { - if(!$(this).is(":visible")){ - $(this).toggle(true) - } - }); - } - - {/script} - {#body} - {#if info:scheduler.running} -
- Scheduler is running - - -
- {#else} -
- Scheduler is paused - - -
- {/if} -
-
- -
- -
-
- - - - - - - - - - - - {#for scheduledMethod in info:schedulerContext.scheduledMethods} - - - - - - {/for} - -
#ScheduleSourceActions
{scheduledMethod_count}. - {#if scheduledMethod.schedules.size > 1} -
    - {#for schedule in scheduledMethod.schedules} -
  1. {#scheduleInfo schedule /}
  2. - {/for} -
- {#else} - {#scheduleInfo scheduledMethod.schedules.iterator.next /} - {/if} -
- {scheduledMethod.declaringClassName}#{scheduledMethod.methodName}() - -
- - -
-
- {/body} -{/include} \ No newline at end of file diff --git a/extensions/scheduler/deployment/src/main/resources/dev-templates/tags/configVal.html b/extensions/scheduler/deployment/src/main/resources/dev-templates/tags/configVal.html deleted file mode 100644 index 34efcea830b08..0000000000000 --- a/extensions/scheduler/deployment/src/main/resources/dev-templates/tags/configVal.html +++ /dev/null @@ -1 +0,0 @@ -{#set val=info:configLookup.apply(it)}{#if val == it}{it}{#else}{it} configured as {val}{/if}{/set} \ No newline at end of file diff --git a/extensions/scheduler/deployment/src/main/resources/dev-templates/tags/scheduleInfo.html b/extensions/scheduler/deployment/src/main/resources/dev-templates/tags/scheduleInfo.html deleted file mode 100644 index 5bc96c5e7f65f..0000000000000 --- a/extensions/scheduler/deployment/src/main/resources/dev-templates/tags/scheduleInfo.html +++ /dev/null @@ -1,28 +0,0 @@ -{#if it.cron} - {#configVal it.cron /} -{#else} - Every {#configVal it.every /} -{/if} -{#if it.identity} - with identity {#configVal it.identity /} - {#if info:scheduler.running} - {#if info:scheduler.isPaused(it.identity)} -
- - - -
- {#else} -
- - - -
- {/if} - {/if} -{/if} -{#if it.delay > 0} - (with delay {it.delay} {it.delayUnit.toString.toLowerCase}) -{#else if !it.delayed.empty} - (delayed for {#configVal it.delayed /}) -{/if} diff --git a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/BodyHandlerBean.java b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/BodyHandlerBean.java deleted file mode 100644 index 755c74b85db70..0000000000000 --- a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/BodyHandlerBean.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.quarkus.scheduler.test.devconsole; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; - -import io.quarkus.runtime.RuntimeValue; -import io.quarkus.runtime.configuration.ConfigInstantiator; -import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; -import io.quarkus.vertx.http.runtime.HttpConfiguration; -import io.quarkus.vertx.http.runtime.VertxHttpRecorder; -import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig; -import io.vertx.core.Handler; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; - -@ApplicationScoped -public class BodyHandlerBean { - - void setup(@Observes Router router) { - HttpConfiguration httpConfiguration = new HttpConfiguration(); - ConfigInstantiator.handleObject(httpConfiguration); - Handler bodyHandler = new VertxHttpRecorder(new HttpBuildTimeConfig(), - new ManagementInterfaceBuildTimeConfig(), - new RuntimeValue<>(httpConfiguration), null) - .createBodyHandler(); - router.route().order(Integer.MIN_VALUE + 1).handler(new Handler() { - @Override - public void handle(RoutingContext routingContext) { - routingContext.request().resume(); - bodyHandler.handle(routingContext); - } - }); - } -} diff --git a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/DevConsoleRunScheduledTaskBodyHandlerTest.java b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/DevConsoleRunScheduledTaskBodyHandlerTest.java deleted file mode 100644 index abf5f89deabfd..0000000000000 --- a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/DevConsoleRunScheduledTaskBodyHandlerTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.quarkus.scheduler.test.devconsole; - -import org.hamcrest.Matchers; -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.test.QuarkusDevModeTest; -import io.restassured.RestAssured; - -public class DevConsoleRunScheduledTaskBodyHandlerTest { - - @RegisterExtension - static final QuarkusDevModeTest config = new QuarkusDevModeTest() - .setArchiveProducer( - () -> ShrinkWrap.create(JavaArchive.class).addClasses(NeverRunTask.class, BodyHandlerBean.class)); - - @Test - public void testInvokeScheduledTask() { - RestAssured.with() - .get("empty") - .then() - .statusCode(200) - .body(Matchers.equalTo("true")); - RestAssured.with().formParam("name", "io.quarkus.scheduler.test.devconsole.NeverRunTask#run") - .redirects().follow(false) - .post("q/dev-v1/io.quarkus.quarkus-scheduler/schedules") - .then() - .statusCode(303); - RestAssured.with() - .get("status") - .then() - .statusCode(200) - .body(Matchers.equalTo("task ran")); - } - -} diff --git a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/DevConsoleRunScheduledTaskTest.java b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/DevConsoleRunScheduledTaskTest.java deleted file mode 100644 index ea8e349950f83..0000000000000 --- a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/DevConsoleRunScheduledTaskTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.quarkus.scheduler.test.devconsole; - -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.quarkus.test.QuarkusDevModeTest; -import io.restassured.RestAssured; - -public class DevConsoleRunScheduledTaskTest { - - @RegisterExtension - static final QuarkusDevModeTest config = new QuarkusDevModeTest() - .withApplicationRoot((jar) -> jar.addClasses(NeverRunTask.class)); - - @Test - public void testInvokeScheduledTask() { - RestAssured.with() - .get("empty") - .then() - .statusCode(200) - .body(Matchers.equalTo("true")); - RestAssured.with().formParam("name", "io.quarkus.scheduler.test.devconsole.NeverRunTask#run") - .redirects().follow(false) - .post("q/dev-v1/io.quarkus.quarkus-scheduler/schedules") - .then() - .statusCode(303); - RestAssured.with() - .get("status") - .then() - .statusCode(200) - .body(Matchers.equalTo("task ran")); - } - -} diff --git a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/NeverRunTask.java b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/NeverRunTask.java deleted file mode 100644 index 9440d55af59e5..0000000000000 --- a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/devconsole/NeverRunTask.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.quarkus.scheduler.test.devconsole; - -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; - -import jakarta.enterprise.event.Observes; - -import io.quarkus.scheduler.Scheduled; -import io.vertx.core.Handler; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; - -public class NeverRunTask { - - private static final LinkedBlockingDeque queue = new LinkedBlockingDeque<>(); - - public void setup(@Observes Router router) { - router.route("/status").blockingHandler(new Handler() { - @Override - public void handle(RoutingContext event) { - try { - event.response().end(queue.poll(10, TimeUnit.SECONDS)); - } catch (InterruptedException e) { - e.printStackTrace(); - event.fail(e); - } - } - }); - router.route("/empty").handler(new Handler() { - @Override - public void handle(RoutingContext event) { - event.response().end(Boolean.toString(queue.isEmpty())); - } - }); - } - - @Scheduled(every = "2h", delay = 2, delayUnit = TimeUnit.HOURS) - public void run() { - queue.add("task ran"); - } - -} diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/devconsole/SchedulerDevConsoleRecorder.java b/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/devconsole/SchedulerDevConsoleRecorder.java deleted file mode 100644 index 7254c807b8334..0000000000000 --- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/devconsole/SchedulerDevConsoleRecorder.java +++ /dev/null @@ -1,173 +0,0 @@ -package io.quarkus.scheduler.runtime.devconsole; - -import java.time.Instant; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.jboss.logging.Logger; - -import io.quarkus.arc.Arc; -import io.quarkus.devconsole.runtime.spi.DevConsolePostHandler; -import io.quarkus.devconsole.runtime.spi.FlashScopeUtil.FlashMessageStatus; -import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.scheduler.ScheduledExecution; -import io.quarkus.scheduler.Scheduler; -import io.quarkus.scheduler.Trigger; -import io.quarkus.scheduler.common.runtime.ScheduledInvoker; -import io.quarkus.scheduler.common.runtime.ScheduledMethod; -import io.quarkus.scheduler.common.runtime.SchedulerContext; -import io.quarkus.scheduler.common.runtime.util.SchedulerUtils; -import io.quarkus.vertx.core.runtime.context.VertxContextSafetyToggle; -import io.smallrye.common.vertx.VertxContext; -import io.vertx.core.Context; -import io.vertx.core.Handler; -import io.vertx.core.MultiMap; -import io.vertx.core.Vertx; -import io.vertx.ext.web.RoutingContext; - -@Recorder -public class SchedulerDevConsoleRecorder { - - private static final Logger LOG = Logger.getLogger(SchedulerDevConsoleRecorder.class); - - public Supplier> getConfigLookup() { - return new Supplier>() { - - @Override - public Function get() { - return SchedulerUtils::lookUpPropertyValue; - } - }; - } - - public Handler invokeHandler() { - // the usual issue of Vert.x hanging on to the first TCCL and setting it on all its threads - final ClassLoader currentCl = Thread.currentThread().getContextClassLoader(); - return new DevConsolePostHandler() { - @Override - protected void handlePost(RoutingContext ctx, MultiMap form) throws Exception { - String action = form.get("action"); - if ("pause".equals(action)) { - Scheduler scheduler = Arc.container().instance(Scheduler.class).get(); - if (scheduler.isRunning()) { - scheduler.pause(); - LOG.info("Scheduler paused via Dev UI"); - flashMessage(ctx, "Scheduler paused"); - } - } else if ("resume".equals(action)) { - Scheduler scheduler = Arc.container().instance(Scheduler.class).get(); - if (!scheduler.isRunning()) { - scheduler.resume(); - LOG.info("Scheduler resumed via Dev UI"); - flashMessage(ctx, "Scheduler resumed"); - } - } else if ("pauseJob".equals(action)) { - Scheduler scheduler = Arc.container().instance(Scheduler.class).get(); - String identity = form.get("identity"); - if (identity != null && !scheduler.isPaused(identity)) { - scheduler.pause(identity); - LOG.infof("Scheduler paused job with identity '%s' via Dev UI", identity); - flashMessage(ctx, "Job with identity " + identity + " paused"); - } - } else if ("resumeJob".equals(action)) { - Scheduler scheduler = Arc.container().instance(Scheduler.class).get(); - String identity = form.get("identity"); - if (identity != null && scheduler.isPaused(identity)) { - scheduler.resume(identity); - LOG.infof("Scheduler resumed job with identity '%s'via Dev UI", identity); - flashMessage(ctx, "Job with identity " + identity + " resumed"); - } - } else { - String name = form.get("name"); - SchedulerContext context = Arc.container().instance(SchedulerContext.class).get(); - for (ScheduledMethod metadata : context.getScheduledMethods()) { - if (metadata.getMethodDescription().equals(name)) { - Vertx vertx = Arc.container().instance(Vertx.class).get(); - Context vdc = VertxContext.getOrCreateDuplicatedContext(vertx); - VertxContextSafetyToggle.setContextSafe(vdc, true); - try { - ScheduledInvoker invoker = context - .createInvoker(metadata.getInvokerClassName()); - if (invoker.isBlocking()) { - vdc.executeBlocking(p -> { - try { - invoker.invoke(new DevModeScheduledExecution()); - } catch (Exception ignored) { - } finally { - p.complete(); - } - }, false); - } else { - vdc.runOnContext(x -> { - try { - invoker.invoke(new DevModeScheduledExecution()); - } catch (Exception ignored) { - } - }); - } - LOG.infof("Invoked scheduled method %s via Dev UI", name); - } catch (Exception e) { - LOG.error( - "Unable to invoke a @Scheduled method: " - + metadata.getMethodDescription(), - e); - } - flashMessage(ctx, "Action invoked"); - return; - } - } - flashMessage(ctx, "Action not found: " + name, FlashMessageStatus.ERROR); - } - } - }; - } - - private static class DevModeScheduledExecution implements ScheduledExecution { - - private final Instant now; - - DevModeScheduledExecution() { - super(); - this.now = Instant.now(); - } - - @Override - public Trigger getTrigger() { - return new Trigger() { - - @Override - public String getId() { - return "dev-console"; - } - - @Override - public Instant getNextFireTime() { - return null; - } - - @Override - public Instant getPreviousFireTime() { - return now; - } - - @Override - public boolean isOverdue() { - return false; - } - - }; - } - - @Override - public Instant getFireTime() { - return now; - } - - @Override - public Instant getScheduledFireTime() { - return now; - } - - } - -} diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleSchedulerSmokeTest.java b/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleSchedulerSmokeTest.java deleted file mode 100644 index d8a4aa04d316a..0000000000000 --- a/integration-tests/devmode/src/test/java/io/quarkus/test/devconsole/DevConsoleSchedulerSmokeTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.quarkus.test.devconsole; - -import java.util.concurrent.TimeUnit; - -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.quarkus.scheduler.Scheduled; -import io.quarkus.test.QuarkusDevModeTest; -import io.restassured.RestAssured; - -/** - * Note that this test cannot be placed under the relevant {@code -deployment} module because then the DEV UI processor would - * not be able to locate the template resources correctly. - */ -public class DevConsoleSchedulerSmokeTest { - - @RegisterExtension - static final QuarkusDevModeTest config = new QuarkusDevModeTest() - .withApplicationRoot((jar) -> jar.addClass(Jobs.class)); - - @Test - public void testScheduler() { - RestAssured.get("q/dev-v1") - .then() - .statusCode(200).body(Matchers.containsString("Scheduled Methods")); - RestAssured.get("q/dev-v1/io.quarkus.quarkus-scheduler/schedules") - .then() - .statusCode(200).body(Matchers.containsString("Scheduler is running")); - } - - public static class Jobs { - - @Scheduled(every = "2h", delay = 2, delayUnit = TimeUnit.HOURS) - public void run() { - } - - } - -}