diff --git a/bom/runtime/pom.xml b/bom/runtime/pom.xml
index 3f1d1a775..6ddc0c50b 100644
--- a/bom/runtime/pom.xml
+++ b/bom/runtime/pom.xml
@@ -127,8 +127,8 @@
1.12.4
3.3.2.Final
0.4.0
- 0.0.12
- 0.0.12
+ 0.0.13
+ 0.0.13
2.4.0
1.0.0.Final
3.4.14
diff --git a/build-parent/pom.xml b/build-parent/pom.xml
index 9b13b0ab3..94a36db3b 100644
--- a/build-parent/pom.xml
+++ b/build-parent/pom.xml
@@ -35,7 +35,7 @@
19.3.1
4.1.1
0.0.12
- 0.0.12
+ 0.0.13
3.8.5
diff --git a/docs/src/main/asciidoc/vertx.adoc b/docs/src/main/asciidoc/vertx.adoc
index 59ace7f08..24cc63e84 100644
--- a/docs/src/main/asciidoc/vertx.adoc
+++ b/docs/src/main/asciidoc/vertx.adoc
@@ -498,6 +498,92 @@ Then, create the native executable with:
./mvnw package -Pnative
----
+== Deploying verticles
+
+https://vertx.io/docs/vertx-core/java/#_verticles[Verticles] is "a simple, scalable, actor-like deployment and concurrency model" provided by _Vert.x_.
+This model does not claim to be a strict actor-model implementation, but it does share similarities especially with respect to concurrency, scaling and deployment.
+To use this model, you write and _deploy_ verticles, communicating with each other by sending messages on the event bus.
+
+You can deploy _verticles_ in Quarkus.
+It supports:
+
+* _bare_ verticle - Java classes extending `io.vertx.core.AbstractVerticle`
+* _Mutiny_ verticle - Java classes extending `io.smallrye.mutiny.vertx.core.AbstractVerticle`
+
+To deploy verticles, use the regular Vert.x API:
+
+[source, java]
+====
+@Inject Vertx vertx;
+
+// ...
+vertx.deployVerticle(MyVerticle.class.getName(), ar -> { });
+vertx.deployVerticle(new MyVerticle(), ar -> { });
+====
+
+You can also pass deployment options to configure the verticle as well as set the number of instances.
+
+Verticles are not _beans_ by default.
+However, you can implement them as _ApplicationScoped_ beans and get injection support:
+
+[source, java]
+====
+package io.quarkus.vertx.verticles;
+
+import io.smallrye.mutiny.Uni;
+import io.smallrye.mutiny.vertx.core.AbstractVerticle;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+import javax.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+public class MyBeanVerticle extends AbstractVerticle {
+
+ @ConfigProperty(name = "address") String address;
+
+ @Override
+ public Uni asyncStart() {
+ return vertx.eventBus().consumer(address)
+ .handler(m -> m.replyAndForget("hello"))
+ .completionHandler();
+ }
+}
+====
+
+You don't have to inject the `vertx` instance but instead leverage the instance stored in the protected field of `AbstractVerticle`.
+
+Then, deploy the verticle instance with:
+
+[source, java]
+====
+package io.quarkus.vertx.verticles;
+
+import io.quarkus.runtime.StartupEvent;
+import io.vertx.mutiny.core.Vertx;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
+
+@ApplicationScoped
+public class VerticleDeployer {
+
+ public void init(@Observes StartupEvent e, Vertx vertx, MyBeanVerticle verticle) {
+ vertx.deployVerticle(verticle).await().indefinitely();
+ }
+}
+====
+
+If you want to deploy every exposed `AbstractVerticle`, you can use:
+
+[source, java]
+====
+public void init(@Observes StartupEvent e, Vertx vertx, Instance verticles) {
+ for (AbstractVerticle verticle : verticles) {
+ vertx.deployVerticle(verticle).await().indefinitely();
+ }
+}
+====
+
== Read only deployment environments
In environments with read only file systems you may receive errors of the form:
diff --git a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java
index e3d66ea15..8f2de83bb 100644
--- a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java
+++ b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java
@@ -49,7 +49,6 @@ import io.quarkus.vertx.ConsumeEvent;
import io.quarkus.vertx.core.deployment.CoreVertxBuildItem;
import io.quarkus.vertx.runtime.VertxProducer;
import io.quarkus.vertx.runtime.VertxRecorder;
-import io.vertx.reactivex.core.AbstractVerticle;
class VertxProcessor {
@@ -158,10 +157,17 @@ class VertxProcessor {
}
@BuildStep
- void registerRxVerticleClasses(CombinedIndexBuildItem indexBuildItem,
+ void registerVerticleClasses(CombinedIndexBuildItem indexBuildItem,
BuildProducer reflectiveClass) {
+ // RX Verticles
for (ClassInfo ci : indexBuildItem.getIndex()
- .getAllKnownSubclasses(DotName.createSimple(AbstractVerticle.class.getName()))) {
+ .getAllKnownSubclasses(DotName.createSimple(io.vertx.reactivex.core.AbstractVerticle.class.getName()))) {
+ reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, ci.toString()));
+ }
+
+ // Mutiny Verticles
+ for (ClassInfo ci : indexBuildItem.getIndex()
+ .getAllKnownSubclasses(DotName.createSimple(io.smallrye.mutiny.vertx.core.AbstractVerticle.class.getName()))) {
reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, ci.toString()));
}
}
diff --git a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/MutinyCodecTest.java b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/MutinyCodecTest.java
new file mode 100644
index 000000000..c3fe70cac
--- /dev/null
+++ b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/MutinyCodecTest.java
@@ -0,0 +1,100 @@
+package io.quarkus.vertx;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.inject.Inject;
+
+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.QuarkusUnitTest;
+import io.smallrye.mutiny.Uni;
+import io.smallrye.mutiny.infrastructure.Infrastructure;
+import io.vertx.mutiny.core.Vertx;
+import io.vertx.mutiny.core.eventbus.Message;
+
+public class MutinyCodecTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap
+ .create(JavaArchive.class).addClasses(MyBean.class, MyNonLocalBean.class,
+ MyPetCodec.class, Person.class, Pet.class));
+
+ @Inject
+ MyBean bean;
+
+ /**
+ * Bean setting the consumption to be non-local.
+ * So, the user must configure the codec explicitly.
+ */
+ @Inject
+ MyNonLocalBean nonLocalBean;
+
+ @Inject
+ Vertx vertx;
+
+ @Test
+ public void testWithGenericCodec() {
+ Greeting hello = vertx.eventBus(). request("person", new Person("bob", "morane"))
+ .onItem().apply(Message::body)
+ .await().indefinitely();
+ assertThat(hello.getMessage()).isEqualTo("Hello bob morane");
+ }
+
+ @Test
+ public void testWithUserCodec() {
+ Greeting hello = vertx.eventBus(). request("pet", new Pet("neo", "rabbit"))
+ .onItem().apply(Message::body)
+ .await().indefinitely();
+ assertThat(hello.getMessage()).isEqualTo("Hello NEO");
+ }
+
+ @Test
+ public void testWithUserCodecNonLocal() {
+ Greeting hello = vertx.eventBus(). request("nl-pet", new Pet("neo", "rabbit"))
+ .onItem().apply(Message::body)
+ .await().indefinitely();
+ assertThat(hello.getMessage()).isEqualTo("Non Local Hello NEO");
+ }
+
+ static class Greeting {
+ private final String message;
+
+ Greeting(String message) {
+ this.message = message;
+ }
+
+ String getMessage() {
+ return message;
+ }
+ }
+
+ static class MyBean {
+ @ConsumeEvent("person")
+ public Uni hello(Person p) {
+ return Uni.createFrom().item(
+ () -> new Greeting("Hello " + p.getFirstName() + " " + p.getLastName()))
+ .emitOn(Infrastructure.getDefaultExecutor());
+ }
+
+ @ConsumeEvent(value = "pet", codec = MyPetCodec.class)
+ public Uni hello(Pet p) {
+ return Uni.createFrom().item(
+ () -> new Greeting("Hello " + p.getName()))
+ .emitOn(Infrastructure.getDefaultExecutor());
+ }
+ }
+
+ static class MyNonLocalBean {
+ @ConsumeEvent(value = "nl-pet", codec = MyPetCodec.class, local = false)
+ public Uni hello(Pet p) {
+ return Uni.createFrom().item(
+ () -> new Greeting("Non Local Hello " + p.getName()))
+ .emitOn(Infrastructure.getDefaultExecutor());
+ }
+ }
+
+}
diff --git a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/devmode/MutinyVerticleClassnameHotReloadTest.java b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/devmode/MutinyVerticleClassnameHotReloadTest.java
new file mode 100644
index 000000000..66914e6e4
--- /dev/null
+++ b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/devmode/MutinyVerticleClassnameHotReloadTest.java
@@ -0,0 +1,53 @@
+package io.quarkus.vertx.devmode;
+
+import java.util.concurrent.CountDownLatch;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
+import javax.inject.Inject;
+
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusDevModeTest;
+import io.restassured.RestAssured;
+import io.vertx.core.Vertx;
+import io.vertx.ext.web.Router;
+
+public class MutinyVerticleClassnameHotReloadTest {
+
+ @RegisterExtension
+ static final QuarkusDevModeTest test = new QuarkusDevModeTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClasses(MyMutinyVerticle.class, BeanDeployingAVerticleFromInstance.class));
+
+ @Test
+ public void testDeploymentOfMutinyVerticleClass() {
+ String resp = RestAssured.get("/").asString();
+ Assertions.assertTrue(resp.startsWith("ok"));
+ test.modifySourceFile(MyMutinyVerticle.class, data -> data.replace("ok", "hello"));
+ resp = RestAssured.get("/").asString();
+ Assertions.assertTrue(resp.startsWith("hello"));
+ String resp2 = RestAssured.get("/").asString();
+ Assertions.assertEquals(resp, resp2);
+ }
+
+ @ApplicationScoped
+ public static class BeanDeployingAVerticleFromInstance {
+ @Inject
+ Vertx vertx;
+
+ public void init(@Observes Router router) throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ vertx.deployVerticle(MyMutinyVerticle.class.getName(),
+ ar -> latch.countDown());
+ router.get("/").handler(rc -> vertx.eventBus(). request("address", "",
+ ar -> rc.response().end(ar.result().body())));
+ latch.await();
+ }
+ }
+
+}
diff --git a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/devmode/MutinyVerticleInstanceHotReloadTest.java b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/devmode/MutinyVerticleInstanceHotReloadTest.java
new file mode 100644
index 000000000..0b8d97d36
--- /dev/null
+++ b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/devmode/MutinyVerticleInstanceHotReloadTest.java
@@ -0,0 +1,53 @@
+package io.quarkus.vertx.devmode;
+
+import java.util.concurrent.CountDownLatch;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
+import javax.inject.Inject;
+
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusDevModeTest;
+import io.restassured.RestAssured;
+import io.vertx.core.Vertx;
+import io.vertx.ext.web.Router;
+
+public class MutinyVerticleInstanceHotReloadTest {
+
+ @RegisterExtension
+ static final QuarkusDevModeTest test = new QuarkusDevModeTest()
+ .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClasses(MyMutinyVerticle.class, BeanDeployingAVerticleFromInstance.class));
+
+ @Test
+ public void testDeploymentOfMutinyVerticleInstance() {
+ String resp = RestAssured.get("/").asString();
+ Assertions.assertTrue(resp.startsWith("ok"));
+ test.modifySourceFile(MyMutinyVerticle.class, data -> data.replace("ok", "hello"));
+ resp = RestAssured.get("/").asString();
+ Assertions.assertTrue(resp.startsWith("hello"));
+ String resp2 = RestAssured.get("/").asString();
+ Assertions.assertEquals(resp, resp2);
+ }
+
+ @ApplicationScoped
+ public static class BeanDeployingAVerticleFromInstance {
+ @Inject
+ Vertx vertx;
+
+ public void init(@Observes Router router) throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ vertx.deployVerticle(new MyMutinyVerticle(),
+ ar -> latch.countDown());
+ router.get("/").handler(rc -> vertx.eventBus(). request("address", "",
+ ar -> rc.response().end(ar.result().body())));
+ latch.await();
+ }
+ }
+
+}
diff --git a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/devmode/MyMutinyVerticle.java b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/devmode/MyMutinyVerticle.java
new file mode 100644
index 000000000..ce8c7c69f
--- /dev/null
+++ b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/devmode/MyMutinyVerticle.java
@@ -0,0 +1,19 @@
+package io.quarkus.vertx.devmode;
+
+import java.util.UUID;
+
+import io.smallrye.mutiny.Uni;
+import io.smallrye.mutiny.vertx.core.AbstractVerticle;
+
+public class MyMutinyVerticle extends AbstractVerticle {
+
+ private final String id = UUID.randomUUID().toString();
+
+ @Override
+ public Uni asyncStart() {
+ return vertx.eventBus().consumer("address")
+ .handler(m -> m.replyAndForget("ok-" + id))
+ .completionHandler();
+ }
+
+}
diff --git a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/verticles/MyBeanVerticle.java b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/verticles/MyBeanVerticle.java
new file mode 100644
index 000000000..26fdf8699
--- /dev/null
+++ b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/verticles/MyBeanVerticle.java
@@ -0,0 +1,22 @@
+package io.quarkus.vertx.verticles;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+import io.smallrye.mutiny.Uni;
+import io.smallrye.mutiny.vertx.core.AbstractVerticle;
+
+@ApplicationScoped
+public class MyBeanVerticle extends AbstractVerticle {
+
+ @ConfigProperty(name = "address")
+ String address;
+
+ @Override
+ public Uni asyncStart() {
+ return vertx.eventBus().consumer(address)
+ .handler(m -> m.replyAndForget("hello"))
+ .completionHandler();
+ }
+}
diff --git a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/verticles/VerticleDeployer.java b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/verticles/VerticleDeployer.java
new file mode 100644
index 000000000..21af385a2
--- /dev/null
+++ b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/verticles/VerticleDeployer.java
@@ -0,0 +1,16 @@
+package io.quarkus.vertx.verticles;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
+
+import io.quarkus.runtime.StartupEvent;
+import io.vertx.mutiny.core.Vertx;
+
+@ApplicationScoped
+public class VerticleDeployer {
+
+ void deploy(@Observes StartupEvent event, Vertx vertx, MyBeanVerticle verticle) {
+ vertx.deployVerticle(verticle).await().indefinitely();
+ }
+
+}
diff --git a/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/verticles/VerticleDeploymentTest.java b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/verticles/VerticleDeploymentTest.java
new file mode 100644
index 000000000..3170b2408
--- /dev/null
+++ b/extensions/vertx/deployment/src/test/java/io/quarkus/vertx/verticles/VerticleDeploymentTest.java
@@ -0,0 +1,36 @@
+package io.quarkus.vertx.verticles;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.inject.Inject;
+
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusUnitTest;
+import io.vertx.mutiny.core.Vertx;
+import io.vertx.mutiny.core.eventbus.Message;
+
+public class VerticleDeploymentTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest config = new QuarkusUnitTest()
+ .setArchiveProducer(() -> ShrinkWrap
+ .create(JavaArchive.class)
+ .addClasses(MyBeanVerticle.class, VerticleDeployer.class)
+ .addAsResource(new StringAsset("address=foo"), "application.properties"));
+
+ @Inject
+ Vertx vertx;
+
+ @Test
+ public void test() {
+ String s = vertx.eventBus(). request("foo", "anyone?")
+ .onItem().apply(Message::body)
+ .await().indefinitely();
+ assertThat(s).isEqualTo("hello");
+ }
+}
diff --git a/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/MutinyAsyncVerticle.java b/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/MutinyAsyncVerticle.java
new file mode 100644
index 000000000..6bf9fe2e3
--- /dev/null
+++ b/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/MutinyAsyncVerticle.java
@@ -0,0 +1,16 @@
+package io.quarkus.it.vertx.verticles;
+
+import io.smallrye.mutiny.Uni;
+import io.smallrye.mutiny.vertx.core.AbstractVerticle;
+
+public class MutinyAsyncVerticle extends AbstractVerticle {
+
+ @Override
+ public Uni asyncStart() {
+ String address = config().getString("id");
+ return vertx.eventBus().consumer(address)
+ .handler(message -> message.replyAndForget("OK-" + address))
+ .completionHandler();
+ }
+
+}
diff --git a/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/VerticleDeployer.java b/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/VerticleDeployer.java
index ff904a178..a7cdf335a 100644
--- a/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/VerticleDeployer.java
+++ b/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/VerticleDeployer.java
@@ -34,6 +34,14 @@ public class VerticleDeployer {
vertx.deployVerticle(RxVerticle.class.getName(), new DeploymentOptions().setConfig(new JsonObject()
.put("id", "rx-classname")))
.thenAccept(x -> latch.countDown());
+
+ vertx.deployVerticle(MutinyAsyncVerticle::new, new DeploymentOptions().setConfig(new JsonObject()
+ .put("id", "mutiny")))
+ .thenAccept(x -> latch.countDown());
+
+ vertx.deployVerticle(MutinyAsyncVerticle.class.getName(), new DeploymentOptions().setConfig(new JsonObject()
+ .put("id", "mutiny-classname")))
+ .thenAccept(x -> latch.countDown());
latch.countDown();
}
diff --git a/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/VerticleEndpoint.java b/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/VerticleEndpoint.java
index abc2dcd1d..54b6b107c 100644
--- a/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/VerticleEndpoint.java
+++ b/integration-tests/vertx/src/main/java/io/quarkus/it/vertx/verticles/VerticleEndpoint.java
@@ -46,4 +46,18 @@ public class VerticleEndpoint {
.thenApply(Message::body);
}
+ @GET
+ @Path("/mutiny")
+ public CompletionStage mutiny() {
+ return vertx.eventBus(). request("mutiny", "")
+ .thenApply(Message::body);
+ }
+
+ @GET
+ @Path("/mutiny-classname")
+ public CompletionStage mutinyWithClassName() {
+ return vertx.eventBus(). request("mutiny-classname", "")
+ .thenApply(Message::body);
+ }
+
}