Building simple Websocket web application using Angular, Vert.x and Fibers

Product Overview Since ‘reactive’ is getting more and more famous, building software through Reactive frameworks or toolkits has become very popular already. What reactive actually means and why Vert.x is probably the best performant reactive toolkit can be easily found in the web. Vert.x site has comprehensive and well structured information how to be really […]

by Nikolay Petkov

July 18, 2018

5 min read

fiber optic 2749588 1280 - Building simple Websocket web application using Angular, Vert.x and Fibers

Product Overview

simple websocketSince ‘reactive’ is getting more and more famous, building software through Reactive frameworks or toolkits has become very popular already. What reactive actually means and why Vert.x is probably the best performant reactive toolkit can be easily found in the web. Vert.x site has comprehensive and well structured information how to be really reactive. Very important part of Vert.x is the event bus. When you structure your application into service classes known as Verticles they communicate using the event bus exchanging messages asynchronously. The event bus can have bridges, allowing other technologies to consume and send messages. For instance, the browser can be an event bus client using the SockJS bridge. More about SockJs event bus can be found here: Vert.x SockJs-event-bus-bridge

In this example we will create clients server communication in websocket style using SockJs event bus provided by Vert.x. SockJs is our websocket, like JavaScript, library we are going to use for client server communication.

Prerequisites

At the moment of writing this example Angular just released version 6.0 and Vert.x 3.5.2 but the used code is pretty simple and straightforward so I don’t think older or newer versions of Angular or Vert.x won’t be compatible.

Dive in the project

Let’s start with our backend. Create simple Maven Java project with whatever IDE you are using. Once the project is created, use this Maven pom file – pom.xml, that provides libraries we need and plugins in order to build and run. Now it is time to create our verticle that will send data to the client and will handle incoming messages from it. We are using only one verticle which in this case is able to cover all we need. Its code is here: MainVerticle. Important part is:

SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(5000);

Here we use one of the many possible websocket options. We just need to add a 5 seconds interval check of the connection.

SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options);

Create handler for websocket communication.

BridgeOptions bo = new BridgeOptions()
            .addInboundPermitted(new PermittedOptions().setAddress(EB_WS_SERVER_ADDRESS))
            .addOutboundPermitted(new PermittedOptions().setAddress(EB_WS_CLIENT_ADDRESS));

Here define addresses for inbound and outbound endpoints that will be used from event bus to deliver  messages to interested side.

sockJSHandler.bridge(bo, Sync.fiberHandler(this::handleEBrequests));

On that step we define method that can receive messages from client. There is another way to receive messages from client using Vert.x eventbus consumer:

vertx.eventBus().consumer(EB_WS_SERVER_ADDRESS, Sync.fiberHandler(this::handleClientMessage));

Finally lets add some testing endpoint to use for sending of some messages from server to client.

@Suspendable
    private void sendMsgToClient(RoutingContext ctx) {
        final JsonObject message;
        if (ctx.getBody() != null && ctx.getBody().length() > 0) {
            message = ctx.getBodyAsJson();
        } else {
            message = new JsonObject().put("msg", "Success");
        }
        vertx.eventBus().publish(EB_WS_CLIENT_ADDRESS, message.encode());
        ctx.response().setStatusCode(HttpResponseStatus.OK.code())
            .putHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON)
            .end(message.toString());
    }

Annotation: @Suspendable might be unknown for you but it is being used from Vert.x to make async code style to behave like sync despite it is not sync. No callback hell, no boilerplate callback code and in the same time same performance. This magic is provided from Vert.x Sync using Fibers.
Rest endpoint for it is: PUT localhost:9090/sendMsgToClient with or without any json body. Source code for backend app can be found here: sockjs-backen-blog.

It is now time to create our Client. As already mentioned for this example we will create Angular 6 App by CLI. How to do it you can read here: Angular quickstart. Source code for client app can be found here: sockjs-web-client-blog. First we need to add only needed dependency: vertx3-eventbus-client. Using npm i vertx3-eventbus-client will add our dependency to the project. For our simple scenario we can use default created AppComponent, that completely covers our needs from Angular perspective.

First we have to import EventBus object that will use:

import * as  EventBus from 'vertx3-eventbus-client';

Since vertx3-eventbus-client library is still not fully TypeScrypt libray we can use it as plain JavaScript Object.

import {Component, OnDestroy, OnInit} from '@angular/core';
import * as  EventBus from 'vertx3-eventbus-client';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
  title = 'app';
  private eb;
  private host = 'https://localhost:9090';

  ngOnInit(): void {
    const self = this;
    self.eb = new EventBus(this.host + '/ws');
    self.eb.onopen = function () {

      // set a handler to receive a message
      self.eb.registerHandler('ws-to-client', function (error, message) {
        console.log('received a message: ' + JSON.stringify(message));
      });
      console.log('Send message: ');
      self.eb.send('ws-to-server', {fruit: 'apple', color: 'red'});
      self.eb.publish('ws-to-server', {fruit: 'grape', color: 'yellow'});
    };
    self.eb.enableReconnect(true);
  }

  ngOnDestroy(): void {
    this.eb.close();
  }
}

Possible problems

At the time of writing of this example not every library is migrated to CLI 6 and it is possible to see errors in your console like: ‘Uncaught ReferenceError: global is not defined’. As a workaround for this issue just add:

‘(window as any).global = window;’ in polifills.ts, this will solve it.

That is all you need to register SockJs eventbus handler, to receive and send messages to the server.

How to test it

To see how our server and client application communicate first we need to start them.

To start Vert.x app go to %YOUR_SERVER_APP_DIRECTORY% and run Maven command: mvn clean package exec:java. In a few seconds Server will be running and listening on port 9090.
In order to start Angular app go to %YOUR_CLIENT_APP_DIRECTORY% and run: ng serve
That will start app in development mode on port 4200 and can be debugged as well.
Another solution is to build Angular app with ng build –prod -aot and copy produced files from ‘dist’ directory into: ‘%YOUR_SERVER_APP_DIRECTORY%srcmainresourceswebroot’. Then your frontend will be accessible on port 9090.
When Client app is successfully started you can see messages in you backend console like:

INFO: A websocket event occurred: SEND, rawMessage: {“type”:”send”,”address”:”ws-to-server”,”headers”:{},”body”:{“fruit”:”apple”,”color”:”red”}}
INFO: A websocket event occurred: PUBLISH, rawMessage: {“type”:”publish”,”address”:”ws-to-server”,”headers”:{},”body”:{“fruit”:”grape”,”color”:”yellow”}}
INFO: A message received: {“fruit”:”apple”,”color”:”red”}
INFO: A message received: {“fruit”:”grape”,”color”:”yellow”}

Using test REST endpoint PUT: localhost:9090/sendMsgToClient you can send message to client that will appear on the browser console.

 

Categories

Java Expert at Dreamix