News - tagged as "vertx"

Weld Vert.x Next?

2017-8-7   vertx , integration   Martin Kouba

Last year Weld team announced the weld-vertx project (see also Weld meets Vert.x and Update on weld-vertx articles). The goal was clear - bring the CDI programming model into the Vert.x ecosystem. Since then, several things have changed. Two new modules were added, CDI 2 and Weld 3 is now used by default, and two final versions were released. I think it’s a good time to summarize the features and plans for future.

What Is It Good For?

First of all, it offers a mature component model for business logic in your Vert.x applications. A reasonable component model helps making your applications maintainable and scalable in terms of development and reusability. So the primary intention is to implement the business logic as CDI beans and use Vert.x APIs for everything else.

Modules

So far there are four modules available:

  1. The Core module starts/stops the Weld SE container and to notifies CDI observer methods when a message is sent via Vert.x event bus. Also you can inject io.vertx.core.Vertx and io.vertx.core.Context in any CDI bean.

  2. The Web module allows to configure the router (a component responsible to find the "logic" to handle an HTTP request) in a declarative way, using @org.jboss.weld.vertx.web.WebRoute annotation. Of course, you can register routes programatically. But what if there are hundreds of routes? The more complicated the REST API is the more difficult it is to maintain the code.

  3. The Service Proxy module makes it possible to inject and invoke service proxies (as defined in https://github.com/vert-x3/vertx-service-proxy).

  4. The Probe module enables Weld Probe development tool in a Vert.x application.

How Do I Use It In My Vert.x Webapp?

Let’s enhance an existing webapp in four simple steps.

1. Project Configuration

Jus add the following dependency to your pom.xml and beans.xml into src/main/resources/META-INF (this will enable CDI).

<dependency>
      <groupId>org.jboss.weld.vertx</groupId>
      <artifactId>weld-vertx-web</artifactId>
      <version>${version.weld-vertx}</version>
    </dependency>
Note
This also brings in org.jboss.weld.vertx:weld-vertx-core, Vert.x and Weld dependencies.

2. Start CDI Container

Deploy WeldWebVerticle and configure router:

class MyVertxApp {
    
         public static void main(String[] args) {
             final Vertx vertx = Vertx.vertx();
             // ...deploy other existing verticles
             final WeldWebVerticle weldVerticle = new WeldWebVerticle();
             vertx.deployVerticle(weldVerticle, result -> {
                 if (result.succeeded()) {
                     vertx.createHttpServer().requestHandler(weldVerticle.createRouter()::accept).listen(8080);
                 }
             });
         }
     }

3. Observe Events

Create a CDI observer method to consume messages from the Vert.x event bus. @VertxConsumer qualifier is used to specify the address the consumer will be registered to. VertxEvent is a wrapper of a Vert.x message.

@ApplicationScoped
    class HelloBean {
    
      void consumerWithDependencies(@Observes @VertxConsumer("hello.address") VertxEvent event, HelloService service) {
        // Reply to the message - io.vertx.core.eventbus.Message.reply(Object)
        event.setReply(service.hello());
      }
    }
Note
Since we’re working with regular observer methods, additional parameters may be declared (next to the event parameter) - these parameters are injection points.

4. Declare Routes

Annotate a class implementing Handler<RoutingContext> with @org.jboss.weld.vertx.web.WebRoute:

@WebRoute("/hello") // Matches all HTTP methods
    class HelloHandler implements Handler<RoutingContext> {
    
        @Inject
        HelloService service;
    
        @Override
        public void handle(RoutingContext ctx) {
            ctx.response().setStatusCode(200).end(service.hello());
        }
    }

This will be translated into something like:

void integrationPseudoCode() {
      HelloHandler hello = produceInjectedInstanceOfHelloHandler();
      Router router = obtainRouterInstance();
      router.route("/hello").handler(hello);
    }
Note
@WebRoute is repeatable, i.e. if multiple annotations are declared on a handler class a single handler instance is used for multiple routes.

5. Enjoy and Celebrate

And that’s it. Fairly straightforward, isn’t it?

Future and Plans

So far there are no new features on the roadmap. The plan is to provide bugfix releases as needed. But weld-vertx is an open source project and so the future is in hands of the community. Feel free to create issues, share ideas, throw feature requests and send pull requests!


Update on weld-vertx

2016-6-21   vertx , integration   Martin Kouba

This is a follow-up on the article Weld meets Vert.x. Weld team has just released the first alpha version of weld-vertx (artifacts are available in Maven Central). Since the last time we talked about weld-vertx two new features were added. First, it’s possible to inject the relevant io.vertx.core.Vertx and io.vertx.core.Context instances direcly into beans. Also there is a new module called weld-vertx-web which extends functionality of weld-vertx-core and io.vertx:vertx-web and allows to register Route handlers discovered during container initialization. In other words, it’s possible to configure a Route in a declarative way:

import javax.inject.Inject;
    
    import org.jboss.weld.vertx.web.WebRoute;
    
    import io.vertx.core.Handler;
    import io.vertx.ext.web.RoutingContext;
    
    // This annotation is used to configure a Route
    @WebRoute("/hello")
    public class HelloHandler implements Handler<RoutingContext> {
    
        @Inject
        SayHelloService service;
    
        @Override
        public void handle(RoutingContext ctx) {
            ctx.response().setStatusCode(200).end(service.hello());
        }
    
    }

The registered handler instances are NOT real bean instances, i.e. they’re not managed by the CDI container (similarly as Java EE components). However, the dependency injection is supported.

The central point of integration is the org.jboss.weld.vertx.web.WeldWebVerticle. This Verticle extends org.jboss.weld.vertx.WeldVerticle and provides the WeldWebVerticle.registerRoutes(Router) method:

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

Give it a try and let us know if you have any idea how to extend the functionality of weld-vertx. Any feedback is appreciated!


Weld meets Vert.x

2016-4-11   vertx , integration   Martin Kouba

Vert.x defines itself as "a toolkit for building reactive applications on the JVM". Sounds cool and trendy. Weld, on the other hand, comes from the Java EE world, based on standards and traditional concepts. But wait, what if we try to combine the two worlds to get the best of them?

Vert.x makes use of a light-weight distributed messaging system to allow application components to communicate in a loosely coupled way. This should sound familiar to all CDI users where beans may produce and consume events as well. Weld team developed a working prototype of Weld/Vert.x integration that allows to automatically register certain observer methods as Vert.x message consumers. A simple echo message consumer could look like this:

import org.jboss.weld.vertx.VertxConsumer;
    import org.jboss.weld.vertx.VertxEvent;
    
    class Foo {
        public void echoConsumer(@Observes @VertxConsumer("test.echo.address") VertxEvent event) {
            event.setReply(event.getMessageBody());
        }
    }
  • @VertxConsumer - a qualifier used to specify the address the consumer will be registered to: test.echo.address

  • VertxEvent - a wrapper of a Vert.x message

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:

public void consumerWithDependencies(@Observes @VertxConsumer("test.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:

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

And how does it work under the hood? First of all, it’s necessary to deploy org.jboss.weld.vertx.WeldVerticle. This Verticle starts Weld SE container and automatically registers org.jboss.weld.vertx.VertxExtension to process all observer methods and detect observers which should become message consumers. Then a special handler is registered for each address to bridge the event bus to the CDI world. Handlers use Vertx.executeBlocking() since we expect the code to be blocking. Later on, whenever a new message is delivered to the handler, Event.fire() is used to notify all relevant observers.

The prototype is surely missing some features. Nevertheless, it shows the CDI programming model might be applicable to the "reactive" world even if not a first class citizen. Moreover, the prototype demonstrates the flexibility of the CDI extension mechanism.

If you want to try it out, you’ll have to clone the https://github.com/weld/weld-vertx repository and build it from source (i.e. run mvn clean install). And if you find it useful feel free to add comments to this blog post. Any feedback is appreciated!