diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index 32b2746d36f920..59331ea7c2f074 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -1797,6 +1797,21 @@
quarkus-undertow-spi
${project.version}
+
+ io.quarkus
+ quarkus-scheduler-api
+ ${project.version}
+
+
+ io.quarkus
+ quarkus-scheduler-common
+ ${project.version}
+
+
+ io.quarkus
+ quarkus-scheduler-kotlin
+ ${project.version}
+
io.quarkus
quarkus-scheduler
diff --git a/extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzScheduler.java b/extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzScheduler.java
index ec283763492e8c..d04ae8ab00a34a 100644
--- a/extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzScheduler.java
+++ b/extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzScheduler.java
@@ -65,14 +65,14 @@
import io.quarkus.scheduler.SkippedExecution;
import io.quarkus.scheduler.SuccessfulExecution;
import io.quarkus.scheduler.Trigger;
-import io.quarkus.scheduler.runtime.ScheduledInvoker;
-import io.quarkus.scheduler.runtime.ScheduledMethodMetadata;
-import io.quarkus.scheduler.runtime.SchedulerContext;
+import io.quarkus.scheduler.common.runtime.ScheduledInvoker;
+import io.quarkus.scheduler.common.runtime.ScheduledMethodMetadata;
+import io.quarkus.scheduler.common.runtime.SchedulerContext;
+import io.quarkus.scheduler.common.runtime.SkipConcurrentExecutionInvoker;
+import io.quarkus.scheduler.common.runtime.SkipPredicateInvoker;
+import io.quarkus.scheduler.common.runtime.StatusEmitterInvoker;
+import io.quarkus.scheduler.common.runtime.util.SchedulerUtils;
import io.quarkus.scheduler.runtime.SchedulerRuntimeConfig;
-import io.quarkus.scheduler.runtime.SkipConcurrentExecutionInvoker;
-import io.quarkus.scheduler.runtime.SkipPredicateInvoker;
-import io.quarkus.scheduler.runtime.StatusEmitterInvoker;
-import io.quarkus.scheduler.runtime.util.SchedulerUtils;
import io.smallrye.common.vertx.VertxContext;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
diff --git a/extensions/scheduler/api/pom.xml b/extensions/scheduler/api/pom.xml
new file mode 100644
index 00000000000000..19a081ddd5585b
--- /dev/null
+++ b/extensions/scheduler/api/pom.xml
@@ -0,0 +1,25 @@
+
+
+
+ io.quarkus
+ quarkus-scheduler-parent
+ 999-SNAPSHOT
+
+ 4.0.0
+
+ quarkus-scheduler-api
+ Quarkus - Scheduler - API
+
+
+
+ io.quarkus
+ quarkus-arc
+
+
+ io.quarkus
+ quarkus-vertx
+
+
+
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/FailedExecution.java b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/FailedExecution.java
similarity index 100%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/FailedExecution.java
rename to extensions/scheduler/api/src/main/java/io/quarkus/scheduler/FailedExecution.java
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Scheduled.java b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/Scheduled.java
similarity index 100%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Scheduled.java
rename to extensions/scheduler/api/src/main/java/io/quarkus/scheduler/Scheduled.java
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/ScheduledExecution.java b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/ScheduledExecution.java
similarity index 100%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/ScheduledExecution.java
rename to extensions/scheduler/api/src/main/java/io/quarkus/scheduler/ScheduledExecution.java
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Scheduler.java b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/Scheduler.java
similarity index 100%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Scheduler.java
rename to extensions/scheduler/api/src/main/java/io/quarkus/scheduler/Scheduler.java
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/SkippedExecution.java b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/SkippedExecution.java
similarity index 100%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/SkippedExecution.java
rename to extensions/scheduler/api/src/main/java/io/quarkus/scheduler/SkippedExecution.java
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/SuccessfulExecution.java b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/SuccessfulExecution.java
similarity index 100%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/SuccessfulExecution.java
rename to extensions/scheduler/api/src/main/java/io/quarkus/scheduler/SuccessfulExecution.java
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Trigger.java b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/Trigger.java
similarity index 100%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Trigger.java
rename to extensions/scheduler/api/src/main/java/io/quarkus/scheduler/Trigger.java
diff --git a/extensions/scheduler/common/pom.xml b/extensions/scheduler/common/pom.xml
new file mode 100644
index 00000000000000..2755b076bb052a
--- /dev/null
+++ b/extensions/scheduler/common/pom.xml
@@ -0,0 +1,41 @@
+
+
+
+ io.quarkus
+ quarkus-scheduler-parent
+ 999-SNAPSHOT
+
+ 4.0.0
+
+ quarkus-scheduler-common
+ Quarkus - Scheduler - Common
+
+
+
+ io.quarkus
+ quarkus-scheduler-api
+
+
+ com.cronutils
+ cron-utils
+
+
+ org.slf4j
+ slf4j-simple
+
+
+ org.glassfish
+ javax.el
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/DefaultInvoker.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DefaultInvoker.java
similarity index 94%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/DefaultInvoker.java
rename to extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DefaultInvoker.java
index 06d07e7e460eab..63ec3717646b8a 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/DefaultInvoker.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DefaultInvoker.java
@@ -1,4 +1,4 @@
-package io.quarkus.scheduler.runtime;
+package io.quarkus.scheduler.common.runtime;
import java.util.concurrent.CompletionStage;
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/DelegateInvoker.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelegateInvoker.java
similarity index 87%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/DelegateInvoker.java
rename to extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelegateInvoker.java
index 725b92147d7492..36ca1d9c66fbdc 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/DelegateInvoker.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/DelegateInvoker.java
@@ -1,4 +1,4 @@
-package io.quarkus.scheduler.runtime;
+package io.quarkus.scheduler.common.runtime;
abstract class DelegateInvoker implements ScheduledInvoker {
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/ScheduledInvoker.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/ScheduledInvoker.java
similarity index 93%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/ScheduledInvoker.java
rename to extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/ScheduledInvoker.java
index 1e775de35d784a..16e9cee3d5e71f 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/ScheduledInvoker.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/ScheduledInvoker.java
@@ -1,4 +1,4 @@
-package io.quarkus.scheduler.runtime;
+package io.quarkus.scheduler.common.runtime;
import java.util.concurrent.CompletionStage;
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/ScheduledMethodMetadata.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/ScheduledMethodMetadata.java
similarity index 95%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/ScheduledMethodMetadata.java
rename to extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/ScheduledMethodMetadata.java
index 565f2fb9664d14..9b90bea8102ed4 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/ScheduledMethodMetadata.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/ScheduledMethodMetadata.java
@@ -1,4 +1,4 @@
-package io.quarkus.scheduler.runtime;
+package io.quarkus.scheduler.common.runtime;
import java.util.List;
@@ -47,4 +47,4 @@ public void setSchedules(List schedules) {
this.schedules = schedules;
}
-}
\ No newline at end of file
+}
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SchedulerContext.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SchedulerContext.java
similarity index 95%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SchedulerContext.java
rename to extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SchedulerContext.java
index 24e67eafc1d672..0c7f8cab98c4d7 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SchedulerContext.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SchedulerContext.java
@@ -1,4 +1,4 @@
-package io.quarkus.scheduler.runtime;
+package io.quarkus.scheduler.common.runtime;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SkipConcurrentExecutionInvoker.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SkipConcurrentExecutionInvoker.java
similarity index 97%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SkipConcurrentExecutionInvoker.java
rename to extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SkipConcurrentExecutionInvoker.java
index 06bc8a80aeba23..1d687a8ad1e61d 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SkipConcurrentExecutionInvoker.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SkipConcurrentExecutionInvoker.java
@@ -1,4 +1,4 @@
-package io.quarkus.scheduler.runtime;
+package io.quarkus.scheduler.common.runtime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SkipPredicateInvoker.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SkipPredicateInvoker.java
similarity index 97%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SkipPredicateInvoker.java
rename to extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SkipPredicateInvoker.java
index c20478b5f7c949..344b36e65394b5 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SkipPredicateInvoker.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/SkipPredicateInvoker.java
@@ -1,4 +1,4 @@
-package io.quarkus.scheduler.runtime;
+package io.quarkus.scheduler.common.runtime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/StatusEmitterInvoker.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/StatusEmitterInvoker.java
similarity index 97%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/StatusEmitterInvoker.java
rename to extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/StatusEmitterInvoker.java
index 50367f5d5cdcc6..46d3eaccf4b59e 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/StatusEmitterInvoker.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/StatusEmitterInvoker.java
@@ -1,4 +1,4 @@
-package io.quarkus.scheduler.runtime;
+package io.quarkus.scheduler.common.runtime;
import java.util.concurrent.CompletionStage;
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/util/SchedulerUtils.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/util/SchedulerUtils.java
similarity index 99%
rename from extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/util/SchedulerUtils.java
rename to extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/util/SchedulerUtils.java
index 3fc871b911f1d3..dbc103f12c0a78 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/util/SchedulerUtils.java
+++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/util/SchedulerUtils.java
@@ -1,4 +1,4 @@
-package io.quarkus.scheduler.runtime.util;
+package io.quarkus.scheduler.common.runtime.util;
import static io.smallrye.common.expression.Expression.Flag.LENIENT_SYNTAX;
import static io.smallrye.common.expression.Expression.Flag.NO_TRIM;
diff --git a/extensions/scheduler/runtime/src/test/java/io/quarkus/scheduler/runtime/util/SchedulerUtilsTest.java b/extensions/scheduler/common/src/test/java/io/quarkus/scheduler/runtime/util/SchedulerUtilsTest.java
similarity index 96%
rename from extensions/scheduler/runtime/src/test/java/io/quarkus/scheduler/runtime/util/SchedulerUtilsTest.java
rename to extensions/scheduler/common/src/test/java/io/quarkus/scheduler/runtime/util/SchedulerUtilsTest.java
index 5d1b8b2aef6c91..a6d9d2fcbcfe72 100644
--- a/extensions/scheduler/runtime/src/test/java/io/quarkus/scheduler/runtime/util/SchedulerUtilsTest.java
+++ b/extensions/scheduler/common/src/test/java/io/quarkus/scheduler/runtime/util/SchedulerUtilsTest.java
@@ -3,6 +3,8 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import io.quarkus.scheduler.common.runtime.util.SchedulerUtils;
+
public class SchedulerUtilsTest {
@Test
diff --git a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/KotlinUtil.java b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/KotlinUtil.java
new file mode 100644
index 00000000000000..8b57af2c6df2df
--- /dev/null
+++ b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/KotlinUtil.java
@@ -0,0 +1,38 @@
+package io.quarkus.scheduler.deployment;
+
+import org.jboss.jandex.MethodInfo;
+import org.jboss.jandex.Type;
+
+final class KotlinUtil {
+
+ private static final Type VOID_CLASS = Type.create(SchedulerDotNames.VOID, Type.Kind.CLASS);
+
+ private KotlinUtil() {
+ }
+
+ static boolean isSuspendMethod(MethodInfo methodInfo) {
+ if (!methodInfo.parameters().isEmpty()) {
+ return methodInfo.parameters().get(methodInfo.parameters().size() - 1).name()
+ .equals(SchedulerDotNames.CONTINUATION);
+ }
+ return false;
+ }
+
+ static Type determineReturnTypeOfSuspendMethod(MethodInfo methodInfo) {
+ Type lastParamType = methodInfo.parameters().get(methodInfo.parameters().size() - 1);
+ if (lastParamType.kind() != Type.Kind.PARAMETERIZED_TYPE) {
+ throw new IllegalStateException("Something went wrong during parameter type resolution - expected "
+ + lastParamType + " to be a Continuation with a generic type");
+ }
+ lastParamType = lastParamType.asParameterizedType().arguments().get(0);
+ if (lastParamType.kind() != Type.Kind.WILDCARD_TYPE) {
+ throw new IllegalStateException("Something went wrong during parameter type resolution - expected "
+ + lastParamType + " to be a Continuation with a generic type");
+ }
+ lastParamType = lastParamType.asWildcardType().superBound();
+ if (lastParamType.name().equals(SchedulerDotNames.KOTLIN_UNIT)) {
+ return VOID_CLASS;
+ }
+ return lastParamType;
+ }
+}
diff --git a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/ScheduledBusinessMethodItem.java b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/ScheduledBusinessMethodItem.java
index 2a5b0d77bdf87f..acce3bce521f43 100644
--- a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/ScheduledBusinessMethodItem.java
+++ b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/ScheduledBusinessMethodItem.java
@@ -25,7 +25,7 @@ public ScheduledBusinessMethodItem(BeanInfo bean, MethodInfo method, List params = method.parameters();
- if (params.size() > 1
- || (params.size() == 1 && !params.get(0).equals(SCHEDULED_EXECUTION_TYPE))) {
+ int maxParamSize = isSuspendMethod ? 2 : 1;
+ if (params.size() > maxParamSize
+ || (params.size() == maxParamSize && !params.get(0).equals(SCHEDULED_EXECUTION_TYPE))) {
errors.add(new IllegalStateException(String.format(
"Invalid scheduled business method parameters %s [method: %s, bean: %s]", params,
method, scheduledMethod.getBean())));
}
- if (!isValidReturnType(method.returnType())) {
- errors.add(new IllegalStateException(
- String.format(
- "Scheduled business method must return void, CompletionStage or Uni [method: %s, bean: %s]",
- method, scheduledMethod.getBean())));
+ if (!isValidReturnType(method)) {
+ if (isSuspendMethod) {
+ errors.add(new IllegalStateException(
+ String.format(
+ "Suspending scheduled business method must return Unit [method: %s, bean: %s]",
+ method, scheduledMethod.getBean())));
+ } else {
+ errors.add(new IllegalStateException(
+ String.format(
+ "Scheduled business method must return void, CompletionStage or Uni [method: %s, bean: %s]",
+ method, scheduledMethod.getBean())));
+ }
}
// Validate cron() and every() expressions
CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(config.cronType));
@@ -233,7 +244,8 @@ void validateScheduledBusinessMethods(SchedulerConfig config, List buildItemBuildProducer) {
+ try {
+ Thread.currentThread().getContextClassLoader().loadClass("kotlinx.coroutines.CoroutineScope");
+ buildItemBuildProducer.produce(AdditionalBeanBuildItem.builder()
+ .addBeanClass("io.quarkus.scheduler.kotlin.runtime.ApplicationCoroutineScope")
+ .setUnremovable().build());
+ } catch (ClassNotFoundException e) {
+ // ignore
+ }
+
+ }
+
}
diff --git a/extensions/scheduler/kotlin/pom.xml b/extensions/scheduler/kotlin/pom.xml
new file mode 100644
index 00000000000000..f7f36ea3270c06
--- /dev/null
+++ b/extensions/scheduler/kotlin/pom.xml
@@ -0,0 +1,114 @@
+
+
+
+ io.quarkus
+ quarkus-scheduler-parent
+ 999-SNAPSHOT
+
+ 4.0.0
+
+ quarkus-scheduler-kotlin
+ Quarkus - Scheduler - Kotlin
+
+
+
+ io.quarkus
+ quarkus-scheduler-common
+
+
+ io.quarkus
+ quarkus-arc
+
+
+ io.quarkus
+ quarkus-vertx
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jdk8
+ true
+
+
+ org.jetbrains.kotlinx
+ kotlinx-coroutines-jdk8
+ true
+
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.basedir}/src/test/kotlin
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+
+
+ compile
+
+ compile
+
+
+
+ test-compile
+
+ test-compile
+
+
+
+
+ ${maven.compiler.target}
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ io.quarkus
+ quarkus-extension-processor
+ ${project.version}
+
+
+
+
+
+
+ default-compile
+ none
+
+
+
+ default-testCompile
+ none
+
+
+ java-compile
+ compile
+
+ compile
+
+
+
+ java-test-compile
+ test-compile
+
+ testCompile
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+ false
+
+
+
+
+
diff --git a/extensions/scheduler/kotlin/src/main/kotlin/io/quarkus/scheduler/kotlin/runtime/AbstractCoroutineInvoker.kt b/extensions/scheduler/kotlin/src/main/kotlin/io/quarkus/scheduler/kotlin/runtime/AbstractCoroutineInvoker.kt
new file mode 100644
index 00000000000000..1ed8030e51c666
--- /dev/null
+++ b/extensions/scheduler/kotlin/src/main/kotlin/io/quarkus/scheduler/kotlin/runtime/AbstractCoroutineInvoker.kt
@@ -0,0 +1,25 @@
+package io.quarkus.scheduler.kotlin.runtime
+
+import io.quarkus.arc.Arc
+import io.quarkus.scheduler.ScheduledExecution
+import io.quarkus.scheduler.common.runtime.ScheduledInvoker
+import io.vertx.core.Vertx
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.async
+import kotlinx.coroutines.future.asCompletableFuture
+import java.util.concurrent.CompletionStage
+
+abstract class AbstractCoroutineInvoker: ScheduledInvoker {
+
+ override fun invoke(execution: ScheduledExecution): CompletionStage {
+ val coroutineScope = Arc.container().instance(ApplicationCoroutineScope::class.java).get()
+ val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let(::VertxDispatcher)
+ ?: throw IllegalStateException("No Vertx context found")
+
+ return coroutineScope.async(context = dispatcher) {
+ invokeBean(execution)
+ }.asCompletableFuture()
+ }
+
+ abstract suspend fun invokeBean(execution: ScheduledExecution): Void
+}
diff --git a/extensions/scheduler/kotlin/src/main/kotlin/io/quarkus/scheduler/kotlin/runtime/ApplicationCoroutineScope.kt b/extensions/scheduler/kotlin/src/main/kotlin/io/quarkus/scheduler/kotlin/runtime/ApplicationCoroutineScope.kt
new file mode 100644
index 00000000000000..dc38c5c7369948
--- /dev/null
+++ b/extensions/scheduler/kotlin/src/main/kotlin/io/quarkus/scheduler/kotlin/runtime/ApplicationCoroutineScope.kt
@@ -0,0 +1,19 @@
+package io.quarkus.scheduler.kotlin.runtime
+
+import io.quarkus.arc.Unremovable
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import javax.annotation.PreDestroy
+import javax.inject.Singleton
+import kotlin.coroutines.CoroutineContext
+
+@Singleton
+class ApplicationCoroutineScope : CoroutineScope, AutoCloseable {
+ override val coroutineContext: CoroutineContext = SupervisorJob()
+
+ @PreDestroy
+ override fun close() {
+ coroutineContext.cancel()
+ }
+}
diff --git a/extensions/scheduler/kotlin/src/main/kotlin/io/quarkus/scheduler/kotlin/runtime/VertxDispatcher.kt b/extensions/scheduler/kotlin/src/main/kotlin/io/quarkus/scheduler/kotlin/runtime/VertxDispatcher.kt
new file mode 100644
index 00000000000000..dfa1008a94dd79
--- /dev/null
+++ b/extensions/scheduler/kotlin/src/main/kotlin/io/quarkus/scheduler/kotlin/runtime/VertxDispatcher.kt
@@ -0,0 +1,24 @@
+package io.quarkus.scheduler.kotlin.runtime
+
+import io.quarkus.arc.Arc
+import io.vertx.core.Context
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlin.coroutines.CoroutineContext
+
+class VertxDispatcher(private val vertxContext: Context) : CoroutineDispatcher() {
+ override fun dispatch(context: CoroutineContext, block: Runnable) {
+ val requestContext = Arc.container().requestContext()
+ vertxContext.runOnContext {
+ if (requestContext.isActive) {
+ block.run()
+ } else {
+ try {
+ requestContext.activate()
+ block.run()
+ } finally {
+ requestContext.terminate()
+ }
+ }
+ }
+ }
+}
diff --git a/extensions/scheduler/pom.xml b/extensions/scheduler/pom.xml
index 8b620bde336771..a763e4e95e6d8b 100644
--- a/extensions/scheduler/pom.xml
+++ b/extensions/scheduler/pom.xml
@@ -16,6 +16,9 @@
deployment
+ api
+ common
+ kotlin
runtime
diff --git a/extensions/scheduler/runtime/pom.xml b/extensions/scheduler/runtime/pom.xml
index 32d063c8b7c8ed..4c4ddf5031ece8 100644
--- a/extensions/scheduler/runtime/pom.xml
+++ b/extensions/scheduler/runtime/pom.xml
@@ -15,25 +15,15 @@
io.quarkus
- quarkus-arc
+ quarkus-scheduler-kotlin
io.quarkus
- quarkus-vertx
+ quarkus-arc
- com.cronutils
- cron-utils
-
-
- org.slf4j
- slf4j-simple
-
-
- org.glassfish
- javax.el
-
-
+ io.quarkus
+ quarkus-vertx
org.jboss.slf4j
@@ -49,12 +39,6 @@
quarkus-vertx-http
true
-
-
- org.junit.jupiter
- junit-jupiter
- test
-
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SchedulerRecorder.java b/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SchedulerRecorder.java
index 5c5a808336d696..76f68553b4110b 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SchedulerRecorder.java
+++ b/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SchedulerRecorder.java
@@ -7,6 +7,8 @@
import com.cronutils.model.CronType;
import io.quarkus.runtime.annotations.Recorder;
+import io.quarkus.scheduler.common.runtime.ScheduledMethodMetadata;
+import io.quarkus.scheduler.common.runtime.SchedulerContext;
@Recorder
public class SchedulerRecorder {
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SimpleScheduler.java b/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SimpleScheduler.java
index c2ea0c5b36bc2d..ae6e282a0e301e 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SimpleScheduler.java
+++ b/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SimpleScheduler.java
@@ -44,7 +44,13 @@
import io.quarkus.scheduler.SkippedExecution;
import io.quarkus.scheduler.SuccessfulExecution;
import io.quarkus.scheduler.Trigger;
-import io.quarkus.scheduler.runtime.util.SchedulerUtils;
+import io.quarkus.scheduler.common.runtime.ScheduledInvoker;
+import io.quarkus.scheduler.common.runtime.ScheduledMethodMetadata;
+import io.quarkus.scheduler.common.runtime.SchedulerContext;
+import io.quarkus.scheduler.common.runtime.SkipConcurrentExecutionInvoker;
+import io.quarkus.scheduler.common.runtime.SkipPredicateInvoker;
+import io.quarkus.scheduler.common.runtime.StatusEmitterInvoker;
+import io.quarkus.scheduler.common.runtime.util.SchedulerUtils;
import io.smallrye.common.vertx.VertxContext;
import io.vertx.core.Context;
import io.vertx.core.Handler;
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
index f5b8de54a70c9e..2f98d3737d4de7 100644
--- 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
@@ -13,10 +13,10 @@
import io.quarkus.scheduler.ScheduledExecution;
import io.quarkus.scheduler.Scheduler;
import io.quarkus.scheduler.Trigger;
-import io.quarkus.scheduler.runtime.ScheduledInvoker;
-import io.quarkus.scheduler.runtime.ScheduledMethodMetadata;
-import io.quarkus.scheduler.runtime.SchedulerContext;
-import io.quarkus.scheduler.runtime.util.SchedulerUtils;
+import io.quarkus.scheduler.common.runtime.ScheduledInvoker;
+import io.quarkus.scheduler.common.runtime.ScheduledMethodMetadata;
+import io.quarkus.scheduler.common.runtime.SchedulerContext;
+import io.quarkus.scheduler.common.runtime.util.SchedulerUtils;
import io.smallrye.common.vertx.VertxContext;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
diff --git a/extensions/spring-scheduled/deployment/src/main/java/io/quarkus/spring/scheduled/deployment/SpringScheduledProcessor.java b/extensions/spring-scheduled/deployment/src/main/java/io/quarkus/spring/scheduled/deployment/SpringScheduledProcessor.java
index b749cf184a6bfe..1871fc89ca1f05 100644
--- a/extensions/spring-scheduled/deployment/src/main/java/io/quarkus/spring/scheduled/deployment/SpringScheduledProcessor.java
+++ b/extensions/spring-scheduled/deployment/src/main/java/io/quarkus/spring/scheduled/deployment/SpringScheduledProcessor.java
@@ -24,8 +24,8 @@
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.FeatureBuildItem;
+import io.quarkus.scheduler.common.runtime.util.SchedulerUtils;
import io.quarkus.scheduler.deployment.ScheduledBusinessMethodItem;
-import io.quarkus.scheduler.runtime.util.SchedulerUtils;
/**
* A simple processor that search for Spring Scheduled annotations in Beans and produce
diff --git a/integration-tests/resteasy-reactive-kotlin/standard/pom.xml b/integration-tests/resteasy-reactive-kotlin/standard/pom.xml
index 1e444780fe8e35..0c3157eb28d088 100644
--- a/integration-tests/resteasy-reactive-kotlin/standard/pom.xml
+++ b/integration-tests/resteasy-reactive-kotlin/standard/pom.xml
@@ -31,6 +31,10 @@
io.quarkus
quarkus-smallrye-fault-tolerance
+
+ io.quarkus
+ quarkus-scheduler
+
io.quarkus
quarkus-kotlin
@@ -147,6 +151,19 @@
+
+ io.quarkus
+ quarkus-scheduler-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
diff --git a/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/ScheduledEndpoint.kt b/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/ScheduledEndpoint.kt
new file mode 100644
index 00000000000000..2109028b7e9cd3
--- /dev/null
+++ b/integration-tests/resteasy-reactive-kotlin/standard/src/main/kotlin/io/quarkus/it/resteasy/reactive/kotlin/ScheduledEndpoint.kt
@@ -0,0 +1,36 @@
+package io.quarkus.it.resteasy.reactive.kotlin
+
+import io.quarkus.scheduler.Scheduled
+import io.quarkus.scheduler.ScheduledExecution
+import kotlinx.coroutines.delay
+import java.util.concurrent.atomic.AtomicInteger
+import javax.ws.rs.GET
+import javax.ws.rs.Path
+import javax.ws.rs.core.Response
+
+@Path("scheduled")
+class ScheduledEndpoint {
+
+ private val num1 = AtomicInteger(0)
+ private val num2 = AtomicInteger(0)
+
+ @Scheduled(every = "0.5s")
+ suspend fun scheduled1() {
+ delay(100)
+ num1.compareAndSet(0, 1)
+ }
+
+ @Scheduled(every = "0.5s")
+ suspend fun scheduled2(scheduledExecution: ScheduledExecution) {
+ delay(100)
+ num2.compareAndSet(0, 1)
+ }
+
+ @Path("num1")
+ @GET
+ fun num1() = Response.status(200 + num1.get()).build()
+
+ @Path("num2")
+ @GET
+ fun num2() = Response.status(200 + num2.get()).build()
+}
diff --git a/integration-tests/resteasy-reactive-kotlin/standard/src/test/kotlin/io/quarkus/it/resteasy/reactive/kotlin/ScheduledEndpointTest.kt b/integration-tests/resteasy-reactive-kotlin/standard/src/test/kotlin/io/quarkus/it/resteasy/reactive/kotlin/ScheduledEndpointTest.kt
new file mode 100644
index 00000000000000..5d2a3c8ac25585
--- /dev/null
+++ b/integration-tests/resteasy-reactive-kotlin/standard/src/test/kotlin/io/quarkus/it/resteasy/reactive/kotlin/ScheduledEndpointTest.kt
@@ -0,0 +1,28 @@
+package io.quarkus.it.resteasy.reactive.kotlin
+
+import io.quarkus.test.junit.QuarkusTest
+import io.restassured.module.kotlin.extensions.Then
+import io.restassured.module.kotlin.extensions.When
+import org.junit.jupiter.api.Test
+
+@QuarkusTest
+class ScheduledEndpointTest {
+
+ @Test
+ fun testScheduledMethodWithNoArg() {
+ When {
+ get("/scheduled/num1")
+ } Then {
+ statusCode(201)
+ }
+ }
+
+ @Test
+ fun testScheduledMethodWithScheduledExecution() {
+ When {
+ get("/scheduled/num2")
+ } Then {
+ statusCode(201)
+ }
+ }
+}