From 4987b691b1b721a3c8492f5839a3d442ee794a9d Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 6 Oct 2023 15:22:48 +0200 Subject: [PATCH] Add validation of execution model annotations The `@Blocking`, `@NonBlocking` and `@RunOnVirtualThread` annotations may only be used on "entrypoint" methods (methods invoked by various frameworks in Quarkus). Using these annotations on methods that can only be invoked by application code is invalid. --- ...utionModelAnnotationsAllowedBuildItem.java | 28 ++++++ .../ExecutionModelAnnotationsConfig.java | 35 +++++++ .../ExecutionModelAnnotationsProcessor.java | 94 +++++++++++++++++++ .../grpc/deployment/GrpcMethodsProcessor.java | 20 ++++ .../ReactiveRoutesMethodsProcessor.java | 21 +++++ .../ExecAnnotationInvalidTest.java | 34 +++++++ .../ExecAnnotationValidTest.java | 30 ++++++ .../deployment/JaxrsMethodsProcessor.java | 35 +++++++ .../deployment/JaxrsMethodsProcessor.java | 35 +++++++ .../deployment/SchedulerMethodsProcessor.java | 21 +++++ .../deployment/GraphqlMethodsProcessor.java | 31 ++++++ .../ReactiveMessagingMethodsProcessor.java | 23 +++++ .../devui/spi/DevUIMethodsProcessor.java | 29 ++++++ 13 files changed, 436 insertions(+) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsAllowedBuildItem.java create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsConfig.java create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsProcessor.java create mode 100644 extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcMethodsProcessor.java create mode 100644 extensions/reactive-routes/deployment/src/main/java/io/quarkus/vertx/web/deployment/ReactiveRoutesMethodsProcessor.java create mode 100644 extensions/reactive-routes/deployment/src/test/java/io/quarkus/execannotations/ExecAnnotationInvalidTest.java create mode 100644 extensions/reactive-routes/deployment/src/test/java/io/quarkus/execannotations/ExecAnnotationValidTest.java create mode 100644 extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/JaxrsMethodsProcessor.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/JaxrsMethodsProcessor.java create mode 100644 extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerMethodsProcessor.java create mode 100644 extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/GraphqlMethodsProcessor.java create mode 100644 extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/ReactiveMessagingMethodsProcessor.java create mode 100644 extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/DevUIMethodsProcessor.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsAllowedBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsAllowedBuildItem.java new file mode 100644 index 00000000000000..b11a0c5c30ef06 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsAllowedBuildItem.java @@ -0,0 +1,28 @@ +package io.quarkus.deployment.execannotations; + +import java.util.Objects; +import java.util.function.Predicate; + +import org.jboss.jandex.MethodInfo; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * Carries a predicate that identifies methods that can have annotations which affect + * the execution model ({@code @Blocking}, {@code @NonBlocking}, {@code @RunOnVirtualThread}). + *

+ * Used to detect wrong usage of these annotations, as they are implemented directly + * by the various frameworks and may only be put on "entrypoint" methods. Placing these + * annotations on methods that can only be invoked by application code is always wrong. + */ +public final class ExecutionModelAnnotationsAllowedBuildItem extends MultiBuildItem { + private final Predicate predicate; + + public ExecutionModelAnnotationsAllowedBuildItem(Predicate predicate) { + this.predicate = Objects.requireNonNull(predicate); + } + + public boolean matches(MethodInfo method) { + return predicate.test(method); + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsConfig.java new file mode 100644 index 00000000000000..c02ac60c2da3ad --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsConfig.java @@ -0,0 +1,35 @@ +package io.quarkus.deployment.execannotations; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +@ConfigRoot(phase = ConfigPhase.BUILD_TIME) +@ConfigMapping(prefix = "quarkus.execution-model-annotations") +public interface ExecutionModelAnnotationsConfig { + /** + * Detection mode of invalid usage of execution model annotations. + *

+ * An execution model annotation is {@code @Blocking}, {@code @NonBlocking} and {@code @RunOnVirtualThread}. + * These annotations may only be used on "entrypoint" methods (methods invoked by various frameworks in Quarkus); + * using them on methods that can only be invoked by application code is invalid. + */ + @WithDefault("fail") + Mode detectionMode(); + + enum Mode { + /** + * Invalid usage of execution model annotations causes build failure. + */ + FAIL, + /** + * Invalid usage of execution model annotations causes warning during build. + */ + WARN, + /** + * No detection of invalid usage of execution model annotations. + */ + DISABLED, + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsProcessor.java new file mode 100644 index 00000000000000..d5fbde2c3b6492 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/execannotations/ExecutionModelAnnotationsProcessor.java @@ -0,0 +1,94 @@ +package io.quarkus.deployment.execannotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.Type; +import org.jboss.logging.Logger; + +import io.quarkus.deployment.SuppressForbidden; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.GeneratedClassBuildItem; +import io.smallrye.common.annotation.Blocking; +import io.smallrye.common.annotation.NonBlocking; +import io.smallrye.common.annotation.RunOnVirtualThread; + +public class ExecutionModelAnnotationsProcessor { + private static final Logger log = Logger.getLogger(ExecutionModelAnnotationsProcessor.class); + + private static final DotName BLOCKING = DotName.createSimple(Blocking.class); + private static final DotName NON_BLOCKING = DotName.createSimple(NonBlocking.class); + private static final DotName RUN_ON_VIRTUAL_THREAD = DotName.createSimple(RunOnVirtualThread.class); + + @BuildStep + void check(BuildProducer ignored, // only to make sure this build step is executed + ExecutionModelAnnotationsConfig config, CombinedIndexBuildItem index, + List predicates) { + + if (config.detectionMode() == ExecutionModelAnnotationsConfig.Mode.DISABLED) { + return; + } + + StringBuilder message = new StringBuilder("\n"); + doCheck(message, index.getIndex(), predicates, BLOCKING); + doCheck(message, index.getIndex(), predicates, NON_BLOCKING); + doCheck(message, index.getIndex(), predicates, RUN_ON_VIRTUAL_THREAD); + + if (message.length() > 1) { + message.append("The @Blocking, @NonBlocking and @RunOnVirtualThread annotations may only be used " + + "on \"entrypoint\" methods (methods invoked by various frameworks in Quarkus)\n"); + message.append("Using the @Blocking, @NonBlocking and @RunOnVirtualThread annotations on methods " + + "that can only be invoked by application code is invalid"); + if (config.detectionMode() == ExecutionModelAnnotationsConfig.Mode.WARN) { + log.warn(message); + } else { + throw new IllegalStateException(message.toString()); + } + } + } + + private void doCheck(StringBuilder message, IndexView index, + List predicates, DotName annotationName) { + + List badMethods = new ArrayList<>(); + for (AnnotationInstance annotation : index.getAnnotations(annotationName)) { + // these annotations may be put on classes too, but we'll ignore that for now + if (annotation.target() != null && annotation.target().kind() == AnnotationTarget.Kind.METHOD) { + MethodInfo method = annotation.target().asMethod(); + for (ExecutionModelAnnotationsAllowedBuildItem predicate : predicates) { + if (!predicate.matches(method)) { + badMethods.add(methodToString(method)); + break; + } + } + } + } + + if (!badMethods.isEmpty()) { + message.append("Wrong usage(s) of @").append(annotationName.withoutPackagePrefix()).append(" found:\n"); + for (String method : badMethods) { + message.append("\t- ").append(method).append("\n"); + } + } + } + + @SuppressForbidden(reason = "Using Type.toString() to build an informative message") + private String methodToString(MethodInfo method) { + StringBuilder result = new StringBuilder(); + result.append(method.declaringClass().name()).append('.').append(method.name()); + StringJoiner joiner = new StringJoiner(", ", "(", ")"); + for (Type parameter : method.parameterTypes()) { + joiner.add(parameter.toString()); + } + result.append(joiner); + return result.toString(); + } +} diff --git a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcMethodsProcessor.java b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcMethodsProcessor.java new file mode 100644 index 00000000000000..ceb4ce90d91ab0 --- /dev/null +++ b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcMethodsProcessor.java @@ -0,0 +1,20 @@ +package io.quarkus.grpc.deployment; + +import java.util.function.Predicate; + +import org.jboss.jandex.MethodInfo; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.execannotations.ExecutionModelAnnotationsAllowedBuildItem; + +public class GrpcMethodsProcessor { + @BuildStep + ExecutionModelAnnotationsAllowedBuildItem grpcMethods() { + return new ExecutionModelAnnotationsAllowedBuildItem(new Predicate() { + @Override + public boolean test(MethodInfo method) { + return method.declaringClass().hasDeclaredAnnotation(GrpcDotNames.GRPC_SERVICE); + } + }); + } +} diff --git a/extensions/reactive-routes/deployment/src/main/java/io/quarkus/vertx/web/deployment/ReactiveRoutesMethodsProcessor.java b/extensions/reactive-routes/deployment/src/main/java/io/quarkus/vertx/web/deployment/ReactiveRoutesMethodsProcessor.java new file mode 100644 index 00000000000000..5bb223f5a0da29 --- /dev/null +++ b/extensions/reactive-routes/deployment/src/main/java/io/quarkus/vertx/web/deployment/ReactiveRoutesMethodsProcessor.java @@ -0,0 +1,21 @@ +package io.quarkus.vertx.web.deployment; + +import java.util.function.Predicate; + +import org.jboss.jandex.MethodInfo; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.execannotations.ExecutionModelAnnotationsAllowedBuildItem; + +public class ReactiveRoutesMethodsProcessor { + @BuildStep + ExecutionModelAnnotationsAllowedBuildItem reactiveRoutesMethods() { + return new ExecutionModelAnnotationsAllowedBuildItem(new Predicate() { + @Override + public boolean test(MethodInfo method) { + return method.hasDeclaredAnnotation(DotNames.ROUTE) + || method.hasDeclaredAnnotation(DotNames.ROUTES); + } + }); + } +} diff --git a/extensions/reactive-routes/deployment/src/test/java/io/quarkus/execannotations/ExecAnnotationInvalidTest.java b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/execannotations/ExecAnnotationInvalidTest.java new file mode 100644 index 00000000000000..2b6f6e0536bf68 --- /dev/null +++ b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/execannotations/ExecAnnotationInvalidTest.java @@ -0,0 +1,34 @@ +package io.quarkus.execannotations; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.common.annotation.Blocking; + +public class ExecAnnotationInvalidTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClasses(MyService.class)) + .assertException(e -> { + assertInstanceOf(IllegalStateException.class, e); + assertTrue(e.getMessage().contains("Wrong usage")); + assertTrue(e.getMessage().contains("MyService.hello()")); + }); + + @Test + public void test() { + fail(); + } + + static class MyService { + @Blocking + String hello() { + return "Hello world!"; + } + } +} diff --git a/extensions/reactive-routes/deployment/src/test/java/io/quarkus/execannotations/ExecAnnotationValidTest.java b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/execannotations/ExecAnnotationValidTest.java new file mode 100644 index 00000000000000..940f101046d703 --- /dev/null +++ b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/execannotations/ExecAnnotationValidTest.java @@ -0,0 +1,30 @@ +package io.quarkus.execannotations; + +import static io.restassured.RestAssured.when; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.vertx.web.Route; +import io.smallrye.common.annotation.Blocking; + +public class ExecAnnotationValidTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClasses(MyService.class)); + + @Test + public void test() { + when().get("/").then().statusCode(200).body(is("Hello world!")); + } + + static class MyService { + @Route(path = "/") + @Blocking + String hello() { + return "Hello world!"; + } + } +} diff --git a/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/JaxrsMethodsProcessor.java b/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/JaxrsMethodsProcessor.java new file mode 100644 index 00000000000000..5c184981a45518 --- /dev/null +++ b/extensions/resteasy-classic/resteasy-common/deployment/src/main/java/io/quarkus/resteasy/common/deployment/JaxrsMethodsProcessor.java @@ -0,0 +1,35 @@ +package io.quarkus.resteasy.common.deployment; + +import java.util.function.Predicate; + +import org.jboss.jandex.MethodInfo; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.execannotations.ExecutionModelAnnotationsAllowedBuildItem; +import io.quarkus.resteasy.common.spi.ResteasyDotNames; + +public class JaxrsMethodsProcessor { + @BuildStep + ExecutionModelAnnotationsAllowedBuildItem jaxrsMethods() { + return new ExecutionModelAnnotationsAllowedBuildItem(new Predicate() { + @Override + public boolean test(MethodInfo method) { + // looking for `@Path` on the declaring class is enough + // to avoid having to process inherited JAX-RS annotations + if (method.declaringClass().hasDeclaredAnnotation(ResteasyDotNames.PATH)) { + return true; + } + + // we currently don't handle custom @HttpMethod annotations, should be fine most of the time + return method.hasDeclaredAnnotation(ResteasyDotNames.PATH) + || method.hasDeclaredAnnotation(ResteasyDotNames.GET) + || method.hasDeclaredAnnotation(ResteasyDotNames.POST) + || method.hasDeclaredAnnotation(ResteasyDotNames.PUT) + || method.hasDeclaredAnnotation(ResteasyDotNames.DELETE) + || method.hasDeclaredAnnotation(ResteasyDotNames.PATCH) + || method.hasDeclaredAnnotation(ResteasyDotNames.HEAD) + || method.hasDeclaredAnnotation(ResteasyDotNames.OPTIONS); + } + }); + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/JaxrsMethodsProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/JaxrsMethodsProcessor.java new file mode 100644 index 00000000000000..8ec28ced232222 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/JaxrsMethodsProcessor.java @@ -0,0 +1,35 @@ +package io.quarkus.resteasy.reactive.common.deployment; + +import java.util.function.Predicate; + +import org.jboss.jandex.MethodInfo; +import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.execannotations.ExecutionModelAnnotationsAllowedBuildItem; + +public class JaxrsMethodsProcessor { + @BuildStep + ExecutionModelAnnotationsAllowedBuildItem jaxrsMethods() { + return new ExecutionModelAnnotationsAllowedBuildItem(new Predicate() { + @Override + public boolean test(MethodInfo method) { + // looking for `@Path` on the declaring class is enough + // to avoid having to process inherited JAX-RS annotations + if (method.declaringClass().hasDeclaredAnnotation(ResteasyReactiveDotNames.PATH)) { + return true; + } + + // we currently don't handle custom @HttpMethod annotations, should be fine most of the time + return method.hasDeclaredAnnotation(ResteasyReactiveDotNames.PATH) + || method.hasDeclaredAnnotation(ResteasyReactiveDotNames.GET) + || method.hasDeclaredAnnotation(ResteasyReactiveDotNames.POST) + || method.hasDeclaredAnnotation(ResteasyReactiveDotNames.PUT) + || method.hasDeclaredAnnotation(ResteasyReactiveDotNames.DELETE) + || method.hasDeclaredAnnotation(ResteasyReactiveDotNames.PATCH) + || method.hasDeclaredAnnotation(ResteasyReactiveDotNames.HEAD) + || method.hasDeclaredAnnotation(ResteasyReactiveDotNames.OPTIONS); + } + }); + } +} diff --git a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerMethodsProcessor.java b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerMethodsProcessor.java new file mode 100644 index 00000000000000..dcb47c2703845d --- /dev/null +++ b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerMethodsProcessor.java @@ -0,0 +1,21 @@ +package io.quarkus.scheduler.deployment; + +import java.util.function.Predicate; + +import org.jboss.jandex.MethodInfo; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.execannotations.ExecutionModelAnnotationsAllowedBuildItem; + +public class SchedulerMethodsProcessor { + @BuildStep + ExecutionModelAnnotationsAllowedBuildItem schedulerMethods() { + return new ExecutionModelAnnotationsAllowedBuildItem(new Predicate() { + @Override + public boolean test(MethodInfo method) { + return method.hasDeclaredAnnotation(SchedulerDotNames.SCHEDULED_NAME) + || method.hasDeclaredAnnotation(SchedulerDotNames.SCHEDULES_NAME); + } + }); + } +} diff --git a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/GraphqlMethodsProcessor.java b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/GraphqlMethodsProcessor.java new file mode 100644 index 00000000000000..a08df63f9f1971 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/GraphqlMethodsProcessor.java @@ -0,0 +1,31 @@ +package io.quarkus.smallrye.graphql.deployment; + +import java.util.function.Predicate; + +import org.eclipse.microprofile.graphql.Mutation; +import org.eclipse.microprofile.graphql.Query; +import org.jboss.jandex.DotName; +import org.jboss.jandex.MethodInfo; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.execannotations.ExecutionModelAnnotationsAllowedBuildItem; +import io.smallrye.graphql.api.Subscription; + +public class GraphqlMethodsProcessor { + private static final DotName QUERY = DotName.createSimple(Query.class); + private static final DotName MUTATION = DotName.createSimple(Mutation.class); + private static final DotName SUBSCRIPTION = DotName.createSimple(Subscription.class); + + @BuildStep + ExecutionModelAnnotationsAllowedBuildItem graphqlMethods() { + return new ExecutionModelAnnotationsAllowedBuildItem(new Predicate() { + @Override + public boolean test(MethodInfo method) { + // maybe just look for `@GraphQLApi` on the declaring class? + return method.hasDeclaredAnnotation(QUERY) + || method.hasDeclaredAnnotation(MUTATION) + || method.hasDeclaredAnnotation(SUBSCRIPTION); + } + }); + } +} diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/ReactiveMessagingMethodsProcessor.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/ReactiveMessagingMethodsProcessor.java new file mode 100644 index 00000000000000..b19916a6a68b63 --- /dev/null +++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/ReactiveMessagingMethodsProcessor.java @@ -0,0 +1,23 @@ +package io.quarkus.smallrye.reactivemessaging.deployment; + +import java.util.function.Predicate; + +import org.jboss.jandex.MethodInfo; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.execannotations.ExecutionModelAnnotationsAllowedBuildItem; + +public class ReactiveMessagingMethodsProcessor { + @BuildStep + ExecutionModelAnnotationsAllowedBuildItem reactiveMessagingMethods() { + return new ExecutionModelAnnotationsAllowedBuildItem(new Predicate() { + @Override + public boolean test(MethodInfo method) { + return method.hasDeclaredAnnotation(ReactiveMessagingDotNames.INCOMING) + || method.hasDeclaredAnnotation(ReactiveMessagingDotNames.INCOMINGS) + || method.hasDeclaredAnnotation(ReactiveMessagingDotNames.OUTGOING) + || method.hasDeclaredAnnotation(ReactiveMessagingDotNames.OUTGOINGS); + } + }); + } +} diff --git a/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/DevUIMethodsProcessor.java b/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/DevUIMethodsProcessor.java new file mode 100644 index 00000000000000..4da4256f8a5b57 --- /dev/null +++ b/extensions/vertx-http/dev-ui-spi/src/main/java/io/quarkus/devui/spi/DevUIMethodsProcessor.java @@ -0,0 +1,29 @@ +package io.quarkus.devui.spi; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +import org.jboss.jandex.DotName; +import org.jboss.jandex.MethodInfo; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.execannotations.ExecutionModelAnnotationsAllowedBuildItem; + +public class DevUIMethodsProcessor { + @BuildStep + ExecutionModelAnnotationsAllowedBuildItem devuiMethods(List rpcProviders) { + Set rpcProviderClasses = new HashSet<>(); + for (JsonRPCProvidersBuildItem rpcProvider : rpcProviders) { + rpcProviderClasses.add(DotName.createSimple(rpcProvider.getJsonRPCMethodProviderClass())); + } + + return new ExecutionModelAnnotationsAllowedBuildItem(new Predicate() { + @Override + public boolean test(MethodInfo method) { + return rpcProviderClasses.contains(method.declaringClass().name()); + } + }); + } +}