Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to Connect a GWT Event Bus to a Vert.X Event Bus

DZone's Guide to

How to Connect a GWT Event Bus to a Vert.X Event Bus

It is strangely satisfying when you open up multiple windows that listen to the same event bus and they tick synchronously to the latest random number.

Free Resource

Migrating from On-Prem to Cloud Middleware? Here’s what Aberdeen Group says leading companies should be considering. Brought to you in partnershp with Liaison Technologies

In this tutorial, we’re first going to create a Vert.X service. This service has a verticle that is exposing a topic on the Vert.X event bus to a (CORS-enabled SockJS) event bus bridge.

After that, we’re wiring a GWT application’s event bus up to this event bus stream and display all these received messages. We’ll be using JsInterop annotated classes to use the Vert.X client javascript libraries.

In all, it is an introduction to Vert.X and how to create custom GWT events and use GWT’s latest JsInterop to glue these two frameworks together.

Verticle What?

Vert.X is a reactive framework that uses an event bus. The smaller components of a Vert.X app then listen to this event bus and react to the events on the bus. They are small services being part of the big service — verticles. This is sometimes referred to as a microservices 2.0 architecture.

Technology Used

We use Gradle for builds — both for the GWT application and the Vert.X service.

Vert.X needs Java 8. I’ve used Groovy to make the Vert.X application, but you can use any of Vert.X’s language bindings. The tutorial uses version 3.3.3 of Vert.X.

The tutorial uses the 2.8 version of GWT. This brings JsInterop about and some Java 8 language features like lambdas, which we’ll be using.

JsInterop

JsInterop is a way for GWT applications to map existing Javascript libraries to annotated GWT Java classes. It also allows GWT application classes to be used in Javascript libraries.

Building the Vert.x Service

You can install and run Vert.X using the excellent SDK man tool. For now, I’m just running it as a JavaExec task.

build.gradle:

apply plugin: 'groovy' //<1>

repositories {
 mavenLocal()
 jcenter()
}

dependencies {
 compile 'io.vertx:vertx-core:3.3.3'
 compile 'io.vertx:vertx-web:3.3.3'
 compile 'org.codehaus.groovy:groovy-all:2.4.7'
}

task run(type: JavaExec) { //<2>
 main = "za.co.dvt.jhb.vertxsockjs.VertxSockJsServer"
 classpath = sourceSets.main.runtimeClasspath
}

First, we apply the Groovy plugin. Then, we have a JavaExec task to run the service straight from the Gradle build.

The Main Class

Our main class sets up the Vert.X instance and deploys our verticle (which bridges the topic on the event bus to a SockJS port). It also then sets up a periodic timer that fires a random integer number every second to the random number topic. A consumer is then added to print out the random number from the topic onto stdout.

VertxSockJsServer.groovy:

package za.co.dvt.jhb.vertxsockjs

import io.vertx.core.Vertx
import io.vertx.core.eventbus.Message

class VertxSockJsServer {
 static void main(args) {
  def v = Vertx.vertx()
  v.deployVerticle new RandomNumberPusherVerticle(), {
    println "Deployed"
   } //<1>

  v.setPeriodic 1000, {
    id ->
    v.eventBus().publish "randomnumber",
    new Random().nextInt()
   } //<2>

  v.eventBus().consumer "randomnumber", {
    Message < Integer > s ->
    println "Next number ${s.body()}"
   } //<3>
 }
}

First, we deploy our verticle with a listener upon finishing. Then, we create the timer and publish to the event bus. Finally, we consume everything from the random number topic and print out to stdout.

The SockJS Bridge

RandomNumberPusherVerticle.groovy:

package za.co.dvt.jhb.vertxsockjs

import io.vertx.core.AbstractVerticle
import io.vertx.ext.web.Router
import io.vertx.ext.web.handler.sockjs.BridgeOptions
import io.vertx.ext.web.handler.sockjs.PermittedOptions
import io.vertx.ext.web.handler.sockjs.SockJSHandler

class RandomNumberPusherVerticle extends AbstractVerticle {
    @Override
    void start() throws Exception {

        def sjs = SockJSHandler.create vertx bridge withBridge, {
            event - >
            event.complete true
        } //<1>
        sjs.socketHandler {
            ev - >
                ev.headers().add("Access-Control-Allow-Origin", "*")
        } //<2>
        def router = Router.router vertx //<3>
        router.route "/randomnumber/*"
        handler sjs //<4>

        def port = 9999
        println "Binding to port $port"
        vertx.createHttpServer().requestHandler router. & accept listen port //<5>
    }

    static getWithBridge() {
        new BridgeOptions()
            .addOutboundPermitted(new PermittedOptions(address: "randomnumber")) //<6>
    }
}
  1. Set up a SockJS protocol handler, bridge it with a bridge, and get asked upon each event whether the event from the event bus must be propagated to the SockJS socket.

  2. This is to add a CORS header.

  3. Make a new router.

  4. Route the router to the SockJS handler for any address http:<host instance>/randomnumber.

  5. Bind to the port and start listening.

  6. We have event bus bridge options. We’re only allowing random numbers for outbound events. With the bridge options, you can also add authentication and inbound directives.

Running the Service

You can run the service from the build script:

gradle run

The GWT Client

The GWT client is a bit more involved. We’re going to create an event handler interface for any GWT event bus listener that wants to listen to these new random number events. Also, we need to create a custom GWT event type from which the GWT event bus is going to use to dispatch these events to its listeners.

Lastly, the glue between GWT’s Java world and the Vert.X event bus Javascript library — a few JsInterop-annotated classes and interfaces.

Building the Client

buildscript {
    repositories {
        jcenter() //<1>
    }
    dependencies {
        classpath 'de.richsource.gradle.plugins:gwt-gradle-plugin:0.6' //<2>
    }
}

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'jetty' //<3>
apply plugin: 'gwt' //<4>

repositories {
        mavenLocal()
        jcenter()
        mavenCentral()
}

dependencies {
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

gwt {
gwtVersion='2.8.0'
modules 'za.co.dvt.jhb.sockjsgwtclient.SockJsGwtClient' //<5>
    logLevel = 'INFO'

    minHeapSize = "512M"; //<6>
    maxHeapSize = "1024M";

}

task jettyDraftWar(type: JettyRunWar) { //<7>
    dependsOn draftWar
    dependsOn.remove('war')
    webApp=draftWar.archivePath
}
  1. Add the gwt-gradle plugin to the classpath. We need the gwt-gradle plugin from Bitcentral.

  2. We’re going to test run the client using Jetty.

  3. Apply the gwt-gradle plugin.

  4. Set up the GWT build and add the needed modules.

  5. Remember these otherwise you'll run into GC or heap problems on compile.

  6. Run the client app in a Jetty container.

Interfacing With the GWT Event Bus

This is the event listener interface.

NewRandomNumberEventHandler.java:

package za.co.dvt.jhb.sockjsgwtclient.client.events;

import com.google.gwt.event.shared.EventHandler;

public interface NewRandomNumberEventHandler extends EventHandler {
    void onNewRandomNumber(int number);  //<1>
}

The method is called on the listener’s side upon getting a new random number from the Vert.X’s event bus.

Event

NewRandomNumberEvent.java:

package za.co.dvt.jhb.sockjsgwtclient.client.events;

import com.google.gwt.event.shared.GwtEvent;

public class NewRandomNumberEvent extends GwtEvent<NewRandomNumberEventHandler> {  //<1>
    public static Type<NewRandomNumberEventHandler> TYPE = new Type<>(); //<2>

    private final int newNumber;

    public NewRandomNumberEvent(int newNumber) { //<3>
        this.newNumber = newNumber;
    }

    @Override
    public Type<NewRandomNumberEventHandler> getAssociatedType() {
        return TYPE;
    }

    @Override
    protected void dispatch(NewRandomNumberEventHandler handler) {
        handler.onNewRandomNumber(newNumber); //<4>
    }

    public int getNewNumber() {
        return newNumber;
    }
}
  1. Chatting to the Vert.x event bus. We need to extend the GwtEvent interface for it to be fired from the GWT event bus.

  2. A static type describing our own custom type of event.

  3. The constructor takes our new random number (sent from Vert.X) as an argument.

  4. Call the registered listeners using our new random number.

HTML

First, we need to have the Vert.X JS libraries added to our index.html file.

index.html:

<head>

    <title>Example</title>

    <script src="//cdn.jsdelivr.net/sockjs/1/sockjs.min.js"></script> <!--1-->
    <script type="text/javascript" language="javascript" src="node_modules/vertx3-eventbus-client/vertx-eventbus.js"></script> <!--2-->

    <script type="text/javascript" language="javascript"
            src="app/app.nocache.js"></script> <!--3-->
</head>
  1. Point to a locally installed vertx-eventbus.JS file (can be installed using NPM).

  2. Point to the SockJS libraries on CDN. 

  3. Load our own GWT application.

Jsinterop Helper Classes

We need to create classes to encapsulate talking to the Vert.X event bus. One is a message and the second one a handler interface that receives this message. We’ve got two extra listeners: one that listens when the connection opens to the event bus and another that listens for when it closes. We then need to encapsulate the actual Vert.X event bus JavaScript class so that we can register our event listeners.

Not Feature-Complete

These five JavaScript-wrapping classes do have more functionality to them. However, for display purposes, I only implemented what I needed for it in order to connect to the Vert.X event bus and fire off GWT events.

The Message

message.java:

package za.co.dvt.jhb.sockjsgwtclient.client.vertx;

import jsinterop.annotations.*;

@JsType(isNative = true, namespace = JsPackage.GLOBAL) //<1>
public class Message<T> { //<2>
    @JsProperty 
    public T body; //<3>
}
  1. Note the Java generic notation to set the type of the.body number.

  2. This is a native JS object in the global namespace.

  3. The.body member returns a generic type T.

The Handler

Handler.java:

package za.co.dvt.jhb.sockjsgwtclient.client.vertx;

import jsinterop.annotations.JsFunction;

@JsFunction
public interface Handler<T> {   //<1>
    void onMesssageReceived(Object error, Message<T> message);  //<2>
}
  1. The interface only has one method. The error message is not mapped to a Java type (hence the java.lang.Object type), but the Message type is.

  2. This is an interface with generic notation T (sets the message.body type).

Event Bus Opening Callback

ConnectionOpened.java:

package za.co.dvt.jhb.sockjsgwtclient.client.vertx;
import jsinterop.annotations.JsFunction;

@JsFunction
public interface ConnectionOpened {
    void onOpen();
}

Event Bus Closing Callback

ConnectionClosed.java:

package za.co.dvt.jhb.sockjsgwtclient.client.vertx;

import jsinterop.annotations.JsFunction;

@JsFunction //<1>
public interface ConnectionClosed {
    void onClosed(Object e);
}

Remember this if you’re implementing a function() {} interface.

The Vert.x Event Bus

VertxEventBus.java:

package za.co.dvt.jhb.sockjsgwtclient.client.vertx;

import jsinterop.annotations.*;

@JsType(isNative = true, name = "EventBus", namespace = JsPackage.GLOBAL) //<1>
public class VertxEventBus {

    public VertxEventBus(String url, Object options) {} //<2>

    public native <T> void registerHandler(String address, Handler<T> handler); //<3>

    @JsProperty(name = "onopen")
    public ConnectionOpened onConnectionOpened; //<4>

    @JsProperty(name = "onclose")
    public ConnectionClosed onConnectionClosed; //<5>

}


  1. We’re encapsulating a native Javascript class, named event bus (in JS scope). It is inside the global Javascript namespace.

  2. Our default constructor. Because it is a native type, the native Javascript constructor gets called.

  3. To register a handler. Note the Java generics annotation that sets the type of the Handler’s Message’s body member.

  4. Callback for when the SockJs connection was opened

  5. Callback for when the connection was closed.

GWT Entry Point

SocksJsGwtCientEntryPoint

package za.co.dvt.jhb.sockjsgwtclient.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import za.co.dvt.jhb.sockjsgwtclient.client.events.NewRandomNumberEvent;
import za.co.dvt.jhb.sockjsgwtclient.client.vertx.VertxEventBus;

class SockJsGwtClientEntryPoint implements EntryPoint {
    private EventBus eventBus; //<1>

    private void registerToLocalhostRandomNumber() {
        VertxEventBus vertxEventBus = new VertxEventBus("http://localhost:9999/randomnumber", new Object()); //<2>
        vertxEventBus.onConnectionClosed = (e) -> RootPanel.get().insert(new Label("Closed"), 0);    //<3>
        vertxEventBus.onConnectionOpened = () -> vertxEventBus.<Number>registerHandler(
                "randomnumber",
                (error, message) -> eventBus.fireEvent(new NewRandomNumberEvent(message.body.intValue())) 
        ); //<4>
    }

    @Override
    public void onModuleLoad() {
        eventBus = new SimpleEventBus();
        eventBus.addHandler(NewRandomNumberEvent.TYPE, (number) -> RootPanel.get().insert(new Label("" + number), 0)); //<5>
        registerToLocalhostRandomNumber();
    }
}

1. The GWTeventbus we’ll be using is instantiated in the onModuleLoad() method.

2.Instantiate a Vert.X event bus listener with an empty options object.

3. Listen to when the connection is closed. Add a "closed" label to the page when it is closed.

4. Listen to when the connection is opened and then start listening in on the event bus' randomnumber address. Fire a new GWT event when receiving a new number from the Vert.X event bus.

5. Listen on the GWT event bus. When you receive a new number, add a label to the top of the page to display it.

Running the Client

You can run the client in draft war mode from the build script:

gradle jettyDraftWar

Or you can run the app in GWT development mode:

gradle gwtDev

Alternatively, you can compile the war and run it in your container of choice:

gradlew war

Summary

It is strangely satisfying when you open up multiple windows that listen to the same event bus and they tick synchronously to the latest random number.

Multiple clients Multiple GWT clients connecting to one Vert.X event bus.

The source code is available here.

Is iPaaS solving the right problems? Not knowing the fundamental difference between iPaaS and iPaaS+ could cost you down the road. Brought to you in partnership with Liaison Technologies.

Topics:
integration ,tutorial ,event bus ,gwt ,vert.x

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}