The primary purpose of weld-vertx is to bring the CDI programming model into the Vert.x ecosystem, i.e. to extend the tool-kit for building reactive applications on the JVM.

Weld is the reference implementation of CDI. It’s part of a community of Red Hat projects.

1. Core

  • Provides WeldVerticle which starts/stops the Weld SE container

  • Makes it possible to notify CDI observer methods when a message is sent via Vert.x event bus

  • Provides @ApplicationScoped beans for io.vertx.core.Vertx and io.vertx.core.Context

  • Allows to deploy Verticles produced/injected by Weld

  • Provides "async" helpers such as AsyncReference and AsyncWorker

Artifact GAV
<dependency>
  <groupId>org.jboss.weld.vertx</groupId>
  <artifactId>weld-vertx-core</artifactId>
  <version>${version.weld-vertx}</version>
</dependency>

1.1. CDI observers and Vert.x message consumers

Vert.x makes use of a light-weight distributed messaging system to allow application components to communicate in a loosely coupled way. weld-vertx-core makes it possible to notify CDI observer methods when a message is sent via Vert.x event bus. A simple echo message consumer example:

import org.jboss.weld.vertx.VertxConsumer;
import org.jboss.weld.vertx.VertxEvent;

class Foo {
    // VertxConsumer - a qualifier used to specify the address of the message consumer
    // VertxEvent - a Vert.x message wrapper
    void echoConsumer(@Observes @VertxConsumer("echo.address") VertxEvent event) {
        event.setReply(event.getMessageBody());
    }
}

Since we’re working with a regular observer method, additional parameters may be declared (next to the event parameter). These parameters are injection points. So it’s easy to declare a message consumer dependencies:

void consumerWithDependencies(@Observes @VertxConsumer("dependencies.address") VertxEvent event, CoolService coolService, StatsService statsService) {
    coolService.process(event.getMessageBody());
    statsService.log(event);
}
Note
If you inject a dependent bean, it will be destroyed when the invocation completes.

Last but not least - an observer may also send/publish messages using the Vert.x event bus:

void consumerStrikesBack(@Observes @VertxConsumer("publish.address") VertxEvent event) {
    event.messageTo("test.huhu.address").publish("huhu");
}

1.1.1. How does it work?

The central point of integration is the org.jboss.weld.vertx.VertxExtension. Its primary task is to find all CDI observer methods that should be notified when a message is sent via io.vertx.core.eventbus.EventBus.

If a Vertx instance is available during CDI bootstrap, then VertxExtension also:

  • registers a Vert.x handler for each address found (whenever a new message is delivered to the handler, Event.fire() is used to notify all observers bound to a specific address)

  • adds custom beans for io.vertx.core.Vertx and io.vertx.core.Context (thereby allowing to inject relevant instances into beans)

Note
Handlers use Vertx.executeBlocking() since we expect the code to be blocking.

org.jboss.weld.vertx.WeldVerticle starts/stops the Weld SE container and registers VertxExtension automatically. However, VertxExtension.registerConsumers(Vertx, Event<Object>) could be also used after the bootstrap, e.g. when a Vertx instance is only available after a CDI container is initialized.

1.2. CDI-powered Verticles

It’s also possible to deploy Verticles produced/injected by Weld, e.g.:

@Dependent
class MyVerticle extends AbstractVerticle {

     @Inject
     Service service;

     @Override
     public void start() throws Exception {
         vertx.eventBus().consumer("my.address").handler(m -> m.reply(service.process(m.body())));
     }
}

class MyApp {
     public static void main(String[] args) {
         Vertx vertx = Vertx.vertx();
         WeldVerticle weldVerticle = new WeldVerticle();
         vertx.deployVerticle(weldVerticle, r -> {
             if (r.succeeded()) {
                 // Deploy Verticle instance produced by Weld
                 MyVerticle myVerticle = weldVerticle.container().select(MyBeanVerticle.class).get();
                 vertx.deployVerticle(myVerticle);
             }
         });
     }
}

1.3. AsyncReference

CDI bean instance creation is synchronous. As a result if you inject a bean whose initialization involves potentially blocking operations (e.g. fetching some data from DB) the creation process is blocked until the dependency is ready. AsyncReference is an asynchronously processed wrapper of an injectable reference (also implements CompletionStage) which allows to finish the client bean creation before a dependency is ready. It’s suitable e.g. when injecting a bean whose creation involves potentially blocking operations:

@ApplicationScoped
class Hello {

    @Inject
    AsynReference<ServiceWithBlockingInit> service;

    CompletionStage<String> hello() {
        return service.thenApply(s -> "Hello" + s.getName() + "!");
    }
}

If there is a producer method whose return type is CompletionStage where the result type matches the required type and has all the required qualifers (according to type-safe resolution rules) then CompletionStage#whenComplete(java.util.function.BiConsumer) is used to process the reference. Otherwise, a worker thread is used so that the processing does not block the event loop thread.

1.4. AsyncWorker

AsyncWorker allows to wrap a synchronous action as an asynchronous computation. The action is performed either as blocking operation using a Vertx worker thread or as non-blocking operation using the Vertx event-loop thread:

@Dependent
public class Hello {

    @Inject
    AsyncWorker worker;

    @Inject
    Service service;

    CompletionStage<String> hello() {
        return worker.performBlocking(service::getMessageFromDb).thenApply(m -> "Hello " + m + "!");
    }
}

It’s also possible to combine AsyncWorker with AsyncReference:

public class HelloCombo {

    @Inject
    AsyncWorker worker;

    @Inject
    AsyncReference<Service> serviceRef;

    CompletionStage<String> hello() {
        return serviceRef.thenCompose(service ->
        // At this point Service is ready
        // But getMessage() is also blocking
        worker.performBlocking(service::getMessage)
                // Finally modify the final message
                .thenApply(m -> "Hello " + m + "!"));
    }

}

2. Web

Allows to configure an io.vertx.ext.web.Route in a declarative way, using @org.jboss.weld.vertx.web.WebRoute annotation.

Artifact GAV
<dependency>
  <groupId>org.jboss.weld.vertx</groupId>
  <artifactId>weld-vertx-core</artifactId>
  <version>${version.weld-vertx}</version>
</dependency>

2.1. Declaring routes in a declarative way

weld-vertx-web extends weld-vertx-core and vertx-web functionality and allows to automatically register Route handlers and observer methods discovered during container initialization. In other words, it’s possible to configure a Route in a declarative way - using org.jboss.weld.vertx.web.WebRoute annotation. The target can be a class which implements Handler<RoutingContext>:

import org.jboss.weld.vertx.web.WebRoute;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

@WebRoute("/hello") // Matches all HTTP methods
class HelloWorldHandler implements Handler<RoutingContext> {

    @Override
    public void handle(RoutingContext ctx) {
        ctx.response().setStatusCode(200).end("Hello world!");
    }
}

Or an observer method which observes RoutingContext:

import org.jboss.weld.vertx.web.WebRoute;
import io.vertx.ext.web.RoutingContext;

@ApplicationScoped
class HelloWorld {

    @WebRoute("/hello") // Matches all HTTP methods
    void hello(@Observes RoutingContext ctx) {
        ctx.response().setStatusCode(200).end("Hello world!");
    }
}

Constructed handler instances are not CDI contextual intances. In other words, they’re not managed by the CDI container (similarly as Java EE components like servlets). However, dependency injection, interceptors and decorators are supported:

import static org.jboss.weld.vertx.web.WebRoute.HandlerType.BLOCKING;
import javax.inject.Inject;
import javax.enterprise.context.control.ActivateRequestContext;

@WebRoute("/hello", type = BLOCKING) // A blocking request handler
public class HelloHandler implements Handler<RoutingContext> {

    @Inject
    SayHelloService service;

    // Interceptor binding used to activate CDI request context within a handle() invocation
    @ActivateRequestContext
    @Override
    public void handle(RoutingContext ctx) {
        ctx.response().setStatusCode(200).end(service.hello());
    }
}

@WebRoute is a repeatable annotation. This means that multiple annotations may be declared on a handler class or an observer method. In this case, a handler instance or an observer method is used for multiple routes.

import org.jboss.weld.vertx.web.WebRoute;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.RoutingContext;

@WebRoute("/hello", methods = HttpMethod.GET)
@WebRoute("/bye")
public class SuperHandler implements Handler<RoutingContext> {

    @Override
    public void handle(RoutingContext ctx) {
        // This method will be invoked upon the same handler instance for both routes
        ctx.response().setStatusCode(200).end("I'm super!");
    }
}

2.2. How does it work?

The central point of the module is the org.jboss.weld.vertx.web.RouteExtension. Its primary task is to find all handler classes and observer methods annotated with @WebRoute and register routes through RouteExtension.registerRoutes(Router).

org.jboss.weld.vertx.web.WeldWebVerticle extends org.jboss.weld.vertx.WeldVerticle, registers RouteExtension automatically, and also provides the WeldWebVerticle.registerRoutes(Router) method (which delegates to RouteExtension):

 class MyApp {

     public static void main(String[] args) {
         Vertx vertx = Vertx.vertx();
         WeldWebVerticle weldVerticle = new WeldWebVerticle();
         vertx.deployVerticle(weldVerticle, result -> {
             if (result.succeeded()) {
                 // Configure the router after Weld bootstrap finished
                 vertx.createHttpServer().requestHandler(weldVerticle.createRouter()::accept).listen(8080);
             }
         });
     }
 }

3. Service Proxy

Artifact GAV
<dependency>
  <groupId>org.jboss.weld.vertx</groupId>
  <artifactId>weld-vertx-service-proxy</artifactId>
  <version>${version.weld-vertx}</version>
</dependency>
Note
weld-vertx-service-proxy does not depend directly on weld-vertx-core. However, the default implementation of org.jboss.weld.vertx.serviceproxy.ServiceProxySupport relies on the functionality provided by weld-vertx-core.

3.1. Injecting Service Proxies

A service proxy interface annotated with io.vertx.codegen.annotations.ProxyGen is automatically discovered and a custom bean with @Dependent scope and org.jboss.weld.vertx.serviceproxy.ServiceProxy qualifier is registered.

@ApplicationScoped
public class EchoServiceConsumer {

    // Injects a service proxy for a service with the given address
    @Inject
    @ServiceProxy("echo-service-address")
    EchoService service;

    @Inject
    @Echo
    Event<String> event;

    public void doEchoBusiness(String value) {
        // By default, the result handler is executed by means of Vertx.executeBlocking()
        // In this case, we fire a CDI event observed by EchoObserver bean
        service.echo(value, (r) -> event.fire(r.result()));
    }

}
Tip
See also EchoServiceProxyTest in the test suite.

4. Probe

  • Allows to use Probe development tool in a Vert.x application

  • Depends on weld-vertx-web

Artifact GAV
<dependency>
  <groupId>org.jboss.weld.vertx</groupId>
  <artifactId>weld-vertx-probe</artifactId>
  <version>${version.weld-vertx}</version>
</dependency>

4.1. How does it work?

Just add weld-vertx-probe to the classpath, set the org.jboss.weld.development system property to true and use WeldWebVerticle to register the routes defined declaratively (as defined in How does it work?).