X

The Visual Builder Cloud Service Blog

Working with Asynchronous Event and Websocket in Visual Builder

Shay Shmeltzer
Director of Product Management - Oracle

Written and Developed by Carlos Casares Oracle Spain

Creating applications that interact with data in a synchronous way in Visual Builder is quite easy. But sometimes your application needs to be notified asynchronously or needs to receive so-called unsolicited message. These are messages that are not part of a response of a previous request, might be received at anytime, and need to be handled accordingly. How would you handle these asynchronous events?

You have several approaches to get notified from an external event:

Using a polling mechanism by means of regular calls initiated from the app/browser every X seconds (or milliseconds or minutes…). This approach is heavy for both the client (app running in your browser) and the server. Another (a bit unknown) approach is Server-Sent Events or subscribing to MQTT topics through the Paho Javascipt library. The one we’re going to focus on is Websockets.

(For more information about polling vs. websockets vs. sse, see here).

Websockets

As per Wikipedia: The WebSocket protocol enables interaction between a web browser (or other client application) and a web server with lower overhead than half-duplex alternatives such as HTTP polling, facilitating real-time data transfer from and to the server.

In brief, you can establish a bi-directional communication channel between your application and a websocket server (which most likely will be different than the web server itself). Such communication is initiated by the web application (and thus, from the browser) and once established, you will have a channel to send (browser-to-server) and receive (server-to-browser) messages. Websockets runs on top of the HTTP protocol and supports plain and SSL/TLS communication. When used with VB, you’ll need to connect to the server using Websockets Secure (or WSS) as your browser will not allow to use plain HTTP/WS in a HTTPS served application.

Websocket is available as default object in HTML that can be easily used in your Javascript code such as:
 

var aWebSocket = new WebSocket(url [, protocols]);

Once created, you just need to set a number of callbacks to attend the unsolicited events you’re going to receive. And the object provides a send() method to send messages from the app/browser to the server. You don’t need to link or include any external library to be able to use Websockets in your application. It is that easy!

Maybe the most challenging part of Websocket programming is the communications error handling. Take into account that once you open a websocket channel, it will remain opened as long as your internet connection is stable. A network glitch or outage, will simply close the connection and your code is responsible to re-establish it again. From a Server point of view, you can’t do anything when the client lost its connection. Remember, the client (app) is the one who initiates it, not the server.
    
There are many ways to implement a Websocket server, in many different languages (Java, Javascript/nodejs, C#, Python, etc).

But, apart from the Websocket object available by default, there are also different client libraries that provide extra features and ease the tasks involved in Websocket programming. One of the most famous and my personal favorite is socket.io, that can be used for both client and websocket server implementation. Actually, if you plan to use socket.io as your client library, you must connect to a socket.io-based server (socket.io library also supports other transports whenever Websocket is not supported by your browser).

Socket.io handles communication outages very well. You, as the app programmer, won’t really need to worry about them (apart from handling an offline situation, of course).

Using Socket.io in Visual Builder

While Oracle Visual Builder does not support a declarative “Websocket object” that can be easily dragged and dropped into your apps, it is very easy to extend your applications writing your JS code or importing third party libraries and extend Visual Builder application with a Websocket channel (using, in my example, the socket.io library).

You just need to follow these simple steps:

1)    Import/link the socket.io client library in your app
2)    Create the socket.io object and establish the connection with your server
3)    Send messages whenever you want or
4)    Receive (unsolicited) messages and trigger a VB Custom Event (which will actually handle the message, and interact with VB objects)

You can add socket.io to your VB app by downloading the socket.io client library and import it in your VB app or by loading it at runtime from a CDN using require.js. You can do so in the JS section of your appplication, flow or page, depending if you want the websocket to be available at app, flow or page level respectively:
 

define(['https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js'], function(io) {

(the CDN link used may vary; just do a Google search for socket.io client library cdn and get the latest version)

Declare the socket variable outside of your prototype functions so that it doesn’t get initialized every time and also you can have multiple JS functions for different purposes that will make use of the Websocket communication:

var socket;

You can handle the incoming messages within your JS code, as part of the callback function. However, you will most likely want to run Action Chains as part of such handling. If so, you will need to get your app EventHelper object that will allow you to fire custom events. Declare it along with the socket variable:

var socket, eventHelper;

and get it in your PageModule (or FlowModule or AppModule) constructor. Such constructor is always part of your default JS code:

var PageModule = function PageModule () {};

enhance it as follows:

var PageModule = function PageModule(context) {

  if (context) {

    eventHelper = context.getEventHelper();

  }

};

Note: PageModule() (or FlowModule() or AppModule()) seems to be invoked several times during initialization, and the context object is not always valid. Thus, check if it really comes in before getting an error when trying to get the EventHelper from an undefined variable.

Then, you need to write a prototype function that will handle all the Websockets communication (connection, disconnection and message reception). If you want to also send messages from your VB app (not part of this example), you can write a separate prototype function that will invoke the send()or emit() methods as follows:

socket.send()

socket.emit()

You can use namespaces when sending (and receiving) messages with the socket.io library. Check the official documentation on that.

This is the complete code for the prototype function in my example:
 

PageModule.prototype.startWebsocket = function(start, hostname, port) {
  if (start == true) {
/** Websocket/socket.io stuff **/
    console.log("***********: " + hostname + ":" + port);
    socket = io("wss://" + hostname + ":" + port, { autoConnect: false, transports: ['websocket'] });
    var messageHandler = function(message) {
      console.log("******* [WEBSOCKET]: " + "Message received: " + JSON.stringify(message));
      if (eventHelper) {
        eventHelper.fireCustomEvent("newMessage", { message: { timestamp: Date().toString(), data: message.data } } );
      }
    };
    socket.on('connect', function() {
      console.log("******* [WEBSOCKET]: " + "CONNECT");
      if (eventHelper) {
        eventHelper.fireCustomEvent("changeWebsocketStatus", { connected: true });
      }
    });
    socket.on('disconnect', function() {
      console.log("******* [WEBSOCKET]: " + "DISCONNECT");
      if (eventHelper) {
        eventHelper.fireCustomEvent("changeWebsocketStatus", { connected: false });
      }
    });
    socket.on('message', messageHandler);    
    socket.connect();
/** Websocket/socket.io stuff **/
  } else {
    if (socket) {
      socket.disconnect();
    }
  }
}

It receives three input parameters:

  • start: a boolean variable that indicates whether I must connect or disconnect the websocket communication
  • hostname: hostname (or IP address) where the socket.io server is located
  • port: port used by the socket.io server

If (start == true) the code tries to establish the connection and define some callbacks that will be invoked under certain events (connect, disconnect, and message in the example):

socket = io("wss://" + hostname + ":" + port, { autoConnect: false, transports: ['websocket'] });

Note how we’re using “wss” as the protocol (equivalent to HTTPS for websockets) as your browser will not allow you to establish a plain connection in an SSL served application. Also, we’re forcing socket.io to use websocket protocol and not to auto connect.  Remember the io object is defined during the requirejs declaration:

define(['https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js'], function(io) {

We define the messageHandler function that will be later linked to the message callback definition. In this function we simply fire a VB custom event named newMessage:
 

var messageHandler = function(message) {

  console.log("******* [WEBSOCKET]: " + "Message received: " + JSON.stringify(message));

  if (eventHelper) {

    eventHelper.fireCustomEvent("newMessage", { message: { timestamp: Date().toString(), data: message.data } } );

  }

};

 

As part of the socket.io sample server implementation, we’re sending the following message structure:

{ data: "any text string" }

And thus, we’re accessing its content through message.data in the above code. Of course, you can send literally whatever you want (binary, plain text, JSON, etc).

Remember you can set your own payload to your custom events, but remember its definition when processing it in the ActionChain. In the example, we’re sending the following JSON object:

{

   message: {

      timestamp: Date().toString(),

      data: message.data

   }

}

We now set the callbacks for the connect and disconnect events, where we will fire two different custom events to update the status in the application:

socket.on('connect', function() {

  console.log("******* [WEBSOCKET]: " + "CONNECT");

  if (eventHelper) {

    eventHelper.fireCustomEvent("changeWebsocketStatus", { connected: true });

  }

});

socket.on('disconnect', function() {

  console.log("******* [WEBSOCKET]: " + "DISCONNECT");

  if (eventHelper) {

    eventHelper.fireCustomEvent("changeWebsocketStatus", { connected: false });

  }

});

Finally, we set the message callback handler and (try to) establish the connection:

socket.on('message', messageHandler);    

socket.connect();

Note we’re not defining any method or handler for reconnections. The magic of socket.io will handle all that for us!

The last piece of code is to disconnect the existing connection (if any) should the start input variable is false:

if (socket) {

  socket.disconnect();

}

And that’s all for the JS part!

The Visual Builder App

Now it’s time to configure the VB application:

1)    Create the variables and UI 
2)    Define custom events
3)    Create the Action Chains that will be triggered by my custom events

1)    Create Variables and UI

We start from an empty VB project where we create a new Web App named wssclientsample, and will use the default template (“None”) and do everything in the pre-created main flow and main-start page. Also, all variables, code, etc, will be at main-start page level.

First, we’re going to create the following variables:
 

Var Name Type Purpose
hostname Strint Binding var for WSS hostname
port Number Binding var for WSS port
status String Binding var for to dispaly cnx status on page
start Boolean Binding var for Switch component to start/stop the connection
message

Array of Object
{ timestamp: "string",
  data: "string" }
 

Object that will contain all received messages
adp

Array Data Provider
Bind Data to message variable

Binding var for the Table component

The page UI is:

Screen Layout

​​​​​​

Components data binding (in order):

Component Data Binding
Input Text $page.variables.hostname
Input Number $page.variables.port
Switch $page.variables.start
Input Text $page.variables.status
Table $page.variables.adp

The Switch component triggers the following Action Chain through the value event, to connect or disconnect the websocket channel:

Switch ActionAction flow

The Call Module Function invokes the JS startWebsocket() method with the following mappings:

Parameter Mapping

2)    Define Custom Events

Within the startWebsocket() JS method, you are triggering some custom events based on some events that happen as part of the websocket implementation:

custom events

Custom Event Name Payload Variable Payload Type Pupose
changeWebsocketStatus connected Boolean Fired when connection status changes
newMessage message Object {"timestamp":"string", "data":"string"} Fired when a new message has been received

Note how the fireCustomEvent() method sends the same payload type as defined in the Custom Event:

eventHelper.fireCustomEvent("changeWebsocketStatus", { connected: true });
eventHelper.fireCustomEvent("newMessage", { message: { timestamp: Date().toString(), data: message.data } } );

 

3)    Create Action Chains that are Triggered by Custom Events

The Custom Events are triggered from the JS code that controls the websocket connection and message reception. We need to implement the Action Chains that will be triggered by those Custom Events:

Custom Event Listeners

Custom Ebent Action Chain Input Type
changeWebsocketStatus setStatus status Boolean
newMessage data message Object:
{"timestamp": "string"
  "data": "string"}

The Action Chain code is very simple, we just assign a variable on each:

setStatus:

status

data:

data

data assign

…and with this, we’re done with all VB stuff!

The Server

Now, you will be wondering which Websocket server your VB app is going to connect to.

As part of this sample, I have written a very simple socket.io-based Websocket server that can be found here:

https://github.com/oraclespainpresales/vbcsWebsocketsServerDemo

If you plan to run it locally, you’ll have to deal with the SSL certificates. Remember that you need to host your websocket with SSL or the browser will reject the connection. I use a valid certificate and load it here in the server code:

const optionsSSL = {
  cert: fs.readFileSync(SSLPATH + "/certificate.fullchain.crt").toString(),
  key: fs.readFileSync(SSLPATH + "/certificate.key").toString()
};

However, you will need to have your own, either a valid one or a self-signed. If the second, you will need to upload it to your VB instance. See here for more info.

Remember to run a npm install to download and install all the npm packages needed.

Once you run your server, remember your IP address/hostname and the port used and type them in accordingly in your VB app:
host and port

When ready, you just need POSTMAN, cURL or any other REST client to send the POST request to the server which will forward it as a websocket message to your VB app:

PostMan call

node side

Live Page

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.