Repository: quarkusio/quarkus
Issue: vert.x web extension does not support RX routing context

The vert.x web extension does not currently support the reactive version of the RoutingContext. Code such as ;
```
...
import io.vertx.reactivex.ext.web.RoutingContext;
...

public class SimpleResource {

    @Route(path = "/simple", methods = GET)
    void getSimple(RoutingContext context) {
...
    }
}
```

fails with;

```
[ERROR] Failed to execute goal io.quarkus:quarkus-maven-plugin:999-SNAPSHOT:build (default) on project vertx-web-quarkus: Failed to build a runnable JAR: Failed to build a runner jar: Failed to augment application classes: Build failure: Build failed due to errors
[ERROR] 	[error]: Build step io.quarkus.arc.deployment.ArcAnnotationProcessor#build threw an exception: java.lang.IllegalStateException: Route handler business method must accept exactly one parameter of type RoutingContext/RoutingExchange: [io.vertx.reactivex.ext.web.RoutingContext] [method: void getSimple(io.vertx.reactivex.ext.web.RoutingContext context), bean:CLASS bean [types=[io.quarkus.test.SimpleResource, java.lang.Object], qualifiers=[@Default, @Any], target=io.quarkus.test.SimpleResource]
```

We should either;
1) support both `io.vertx.reactivex.ext.web.RoutingContext` and `io.vertx.ext.web.RoutingContext`

or

2) Raise a warning if `io.vertx.reactivex.ext.web.RoutingContext`  is passed as a `@Route` method parameter type

Changed files:
extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java
extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/Route.java
extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/SimpleRouteTest.java
extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.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 5e540cb7dbf..928f3720c50 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
@@ -171,13 +171,13 @@ public void validate(ValidationContext validationContext) {
                                 if (params.size() > 1
                                         || (params.size() == 1 && !params.get(0).equals(SCHEDULED_EXECUTION_TYPE))) {
                                     throw new IllegalStateException(String.format(
-                                            "Invalid scheduled business method parameters %s [method: %s, bean:%s", params,
+                                            "Invalid scheduled business method parameters %s [method: %s, bean: %s]", params,
                                             method, bean));
                                 }
                                 if (!method.returnType().kind().equals(Type.Kind.VOID)) {
                                     throw new IllegalStateException(
-                                            String.format("Scheduled business method must return void [method: %s, bean:%s",
-                                                    method.returnType(), method, bean));
+                                            String.format("Scheduled business method must return void [method: %s, bean: %s]",
+                                                    method, bean));
                                 }
                                 // Validate cron() and every() expressions
                                 for (AnnotationInstance scheduled : schedules) {
diff --git a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java
index 3a97636f25d..bc078ea639d 100644
--- a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java
+++ b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java
@@ -64,6 +64,8 @@ class VertxWebProcessor {
     private static final DotName ROUTE = DotName.createSimple(Route.class.getName());
     private static final DotName ROUTES = DotName.createSimple(Route.Routes.class.getName());
     private static final DotName ROUTING_CONTEXT = DotName.createSimple(RoutingContext.class.getName());
+    private static final DotName RX_ROUTING_CONTEXT = DotName
+            .createSimple(io.vertx.reactivex.ext.web.RoutingContext.class.getName());
     private static final DotName ROUTING_EXCHANGE = DotName.createSimple(RoutingExchange.class.getName());
     private static final String HANDLER_SUFFIX = "_RouteHandler";
 
@@ -93,13 +95,13 @@ public void validate(ValidationContext validationContext) {
                             List<AnnotationInstance> routes = new LinkedList<>();
                             AnnotationInstance routeAnnotation = annotationStore.getAnnotation(method, ROUTE);
                             if (routeAnnotation != null) {
-                                validateMethodParameters(bean, method);
+                                validateMethod(bean, method);
                                 routes.add(routeAnnotation);
                             }
                             if (routes.isEmpty()) {
                                 AnnotationInstance routesAnnotation = annotationStore.getAnnotation(method, ROUTES);
                                 if (routesAnnotation != null) {
-                                    validateMethodParameters(bean, method);
+                                    validateMethod(bean, method);
                                     Collections.addAll(routes, routesAnnotation.value().asNestedArray());
                                 }
                             }
@@ -169,12 +171,23 @@ public void transform(TransformationContext context) {
         });
     }
 
-    private void validateMethodParameters(BeanInfo bean, MethodInfo method) {
+    private void validateMethod(BeanInfo bean, MethodInfo method) {
+        if (!method.returnType().kind().equals(Type.Kind.VOID)) {
+            throw new IllegalStateException(
+                    String.format("Route handler business method must return void [method: %s, bean: %s]", method, bean));
+        }
         List<Type> params = method.parameters();
-        if (params.size() != 1
-                || !(params.get(0).name().equals(ROUTING_CONTEXT) || params.get(0).name().equals(ROUTING_EXCHANGE))) {
+        boolean hasInvalidParam = true;
+        if (params.size() == 1) {
+            DotName paramTypeName = params.get(0).name();
+            if (ROUTING_CONTEXT.equals(paramTypeName) || RX_ROUTING_CONTEXT.equals(paramTypeName)
+                    || ROUTING_EXCHANGE.equals(paramTypeName)) {
+                hasInvalidParam = false;
+            }
+        }
+        if (hasInvalidParam) {
             throw new IllegalStateException(String.format(
-                    "Route handler business method must accept exactly one parameter of type RoutingContext/RoutingExchange: %s [method: %s, bean:%s",
+                    "Route handler business method must accept exactly one parameter of type RoutingContext/RoutingExchange: %s [method: %s, bean: %s]",
                     params, method, bean));
         }
     }
@@ -223,6 +236,12 @@ private String generateHandler(BeanInfo bean, MethodInfo method, ClassOutput cla
             paramHandle = invoke.getMethodParam(0);
             methodDescriptor = MethodDescriptor.ofMethod(bean.getImplClazz().name().toString(), method.name(), void.class,
                     RoutingContext.class);
+        } else if (method.parameters().get(0).name().equals(RX_ROUTING_CONTEXT)) {
+            paramHandle = invoke.newInstance(
+                    MethodDescriptor.ofConstructor(io.vertx.reactivex.ext.web.RoutingContext.class, RoutingContext.class),
+                    invoke.getMethodParam(0));
+            methodDescriptor = MethodDescriptor.ofMethod(bean.getImplClazz().name().toString(), method.name(), void.class,
+                    io.vertx.reactivex.ext.web.RoutingContext.class);
         } else {
             paramHandle = invoke.newInstance(MethodDescriptor.ofConstructor(RoutingExchangeImpl.class, RoutingContext.class),
                     invoke.getMethodParam(0));
diff --git a/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/SimpleRouteTest.java b/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/SimpleRouteTest.java
index f82b0d83ae6..49e0bcc9c02 100644
--- a/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/SimpleRouteTest.java
+++ b/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/SimpleRouteTest.java
@@ -33,6 +33,7 @@ public class SimpleRouteTest {
     @Test
     public void testSimpleRoute() {
         RestAssured.when().get("/hello").then().statusCode(200).body(is("Hello world!"));
+        RestAssured.when().get("/rx-hello").then().statusCode(200).body(is("Hello world!"));
         RestAssured.when().get("/bzuk").then().statusCode(200).body(is("Hello world!"));
         RestAssured.when().get("/hello-event-bus?name=ping").then().statusCode(200).body(is("Hello PING!"));
         RestAssured.when().get("/foo?name=foo").then().statusCode(200).body(is("Hello foo!"));
@@ -52,6 +53,12 @@ void hello(RoutingContext context) {
             context.response().setStatusCode(200).end("Hello " + (name != null ? name : "world") + "!");
         }
 
+        @Route(path = "/rx-hello")
+        void rxHello(io.vertx.reactivex.ext.web.RoutingContext context) {
+            String name = context.request().getParam("name");
+            context.response().setStatusCode(200).end("Hello " + (name != null ? name : "world") + "!");
+        }
+
         @Route(path = "/bzuk")
         void bzuk(RoutingExchange exchange) {
             exchange.ok("Hello " + exchange.getParam("name").orElse("world") + "!");
diff --git a/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/Route.java b/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/Route.java
index b3b64fb0d0a..6112fb2665f 100644
--- a/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/Route.java
+++ b/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/Route.java
@@ -14,8 +14,8 @@
 
 /**
  * Annotation used to configure a {@link io.quarkus.vertx.web.Route} in a declarative way.
- * 
- * The target business method must accept exacly one argument of type {@link RoutingContext}.
+ * <p>
+ * The target business method must return {@code void} and accept exacly one argument of type {@link RoutingContext}.
  */
 @Repeatable(Routes.class)
 @Retention(RetentionPolicy.RUNTIME)