Skip to Content
Technical Articles

WebSocket’s in SAP UI5 Multi Target Applications consuming Enterprise Messaging 📨

Hi UI5ers!

In this blog post we will have a look on how we can combine a Node.js Application, an Approuter and an SAPUI5 application in a Multi Target Application using WebSocket’s.

This is a Blog series with the following topics in separated Blog posts:

Cloud Foundry Enterprise Messaging Webhooks 📨
Send AMQP messages from CPI to Enterprise Messaging and Consume them in a Node.js AMQP Application 📨
WebSocket’s in SAP UI5 Multi Target Applications consuming Enterprise Messaging 📨 (this blog)

 

Introduction

In this blog post we are going to reuse the code of our previously created “em-consumer” application.

This earlier created application is based and reused from the SAP HANA Academy.

More information about their very clear demo tutorials can be found here:

Repository:

git clone https://github.com/saphanaacademy/em-consumer.git

More information about this consuming application can be found here:

The GitHub repository of this application from the SAP HANA Academy can be found here: https://github.com/saphanaacademy/em-consumer

If you want to catch up with the previous blog, where we reused this code and made some adjustment, you can find it in Send AMQP messages from CPI to Enterprise Messaging and Consume them in a Node.js AMQP Application 📨.

Since this blog will cover the development and implementation of WebSocket’s, more information can be found in the following blog post: https://blogs.sap.com/2018/01/23/how-to-use-websockets-in-the-cloud-foundry-environment-in-the-sap-cloud-platform/

The full code of this Multi Target Application we will be building in this blog, can be found in the following repository: https://github.com/vvdries/MtaEmUI5websocket

 

Building the project

I will be using the SAP Business Application Studio for the development of this application. I would recommend using this editor or Visual Code if you want to develop locally.

Perform the following steps to setup and build your project. Indeed time to ope the terminal!

1. Create a directory called “MtaEmUI5websocket“.

mkdir MtaEmUI5websocket

2. Navigate into this created directory.

cd MtaEmUI5websocket

3. Initialize a “Cloud Foundry HTML5 Application Repository” project.

Yo easy-ui5

Pass the following configuration values to it:

  • How do you want to name this project? ui5Websockets
  • Which namespace do you want to use? blogs
  • On which platform would you like to host the application? Cloud Foundry HTML5 Application Repository
  • Which view type do you want to use? XML
  • How do you want to name your main view? Error
  • Where should your UI5 libs be served from? Content delivery network (SAPUI5)
  • Would you like to create a new directory for the project? No

4. Create a directory for your Node.js application inside your mta project and call it “NodeWsEm“.

mkdir NodeWsEm

5. Navigate into this create directory.

cd NodeWsEm

6. Inside this directory initialize npm.

npm init

Press enter for all the default configuration parameters.

7. create an “index.js” file inside this “NodeWsEm” directory.

touch index.js

8. Copy paste the following code into it:

const WebSocketServer = require('ws').Server;
	//We will create the websocket server on the port given by Cloud Foundry --> Port 8080
	const ws = new WebSocketServer({
		port: process.env.PORT || 8080
	});
	
	var cfenv = require('cfenv');
	var appEnv = cfenv.getAppEnv();
	var emCreds = appEnv.getServiceCreds("EnterpriseMessaging");
	var emCredsM = emCreds.messaging.filter(em => em.protocol[0] === 'amqp10ws');
	const options = {
		uri: emCredsM[0].uri,
		oa2: {
			endpoint: emCredsM[0].oa2.tokenendpoint,
			client: emCredsM[0].oa2.clientid,
			secret: emCredsM[0].oa2.clientsecret
		},
		data: {
			source: "queue:ErrorQueue",
			payload: new Buffer.allocUnsafe(20),
			maxCount: 100,
			logCount: 10
		}
	};

	const { Client } = require('@sap/xb-msg-amqp-v100');
	const client = new Client(options);
	const stream = client.receiver('ErrorQueue').attach(options.data.source);

	ws.on('connection', function (socket) {
		socket.send(JSON.stringify({
			"message": "Hi, this is the Echo-Server"
		}));
	
		stream.on('data', (message) => {
			var payload = JSON.parse(message.payload.toString('utf8'));
			console.log(`Received the following message: ${JSON.stringify(payload)}`);
			socket.send(JSON.stringify(payload));
			message.done();
		});
	
		client
			.on('connected', (destination, peerInfo) => {
				console.log('Connected!');
			})
			.on('assert', (error) => {
				console.log(error.message);
			})
			.on('error', (error) => {
				console.log(error);
			})
			.on('disconnected', (hadError, byBroker, statistics) => {
				console.log('Disconnected!');
			});
	

		client.connect();
	});
  

In this code you will see the SAP HANA Academy code is reused to connect to Enterprise messaging. Via that connection we are notified once messages were created and send to the queue. This was also implemented and tested in the previous blog.

But this code was a little extended, to support and add the WebSocket protocol.

WebSocket’s are implemented here and our UI5 application will connect to this WebSocket later on.

This means that Enterprise Messaging is notifying the Node.js application and the WebSocket inside this Node app will notify the UI5 Application on his turn.

First of all, we need to install the required dependencies/packages. Run the following NPM commands to install these packages:

npm i express		
npm i cfenv		
npm i @sap/xb-msg-amqp-v100
npm i ws

Once you installed the packages, they will be visible in your “package.json” file inside your Node app. Do not forget to add the start script in here.

This code does not differ that much since the previous blog. The only adjustments made here are regarding the WebSocket.

To import and create a WebSocket, the following code has been added to the top of our “index.js file“.

Once the WebSocket Server has been created, the connection to the Enterprise Messaging Queue should still be maintained. This is done by the following code (just like in the previous blog and like it was explained by the SAP HANA Academy):

With this a WebSocket Server has been created and a connection the Enterprise Messaging Service has been established.

Now the only thing left to do is to setup the connections for the WebSocket.

This is done via the “ws.on(‘connection..’”, it allows us to listen for devices that can connect to the WebSocket.

Once they connect, they will receive the message “Hi, this is the Echo-Sevrer”.

As you can see, the “stream.on(‘data’” part has been removed inside our WebSocket. By this we are able to retrieve the data (coming from the Enterprise Messaging Service) inside our WebSocket. Once received this data is send to the socket via the “socket.send” function. This way all the connected devices will receive the incoming Enterprise Messaging message.

The next thing we want to do is to setup our “mta.yaml” file correctly. Not that it is not correct at the very moment, but we need to add our Node Module to it. This along with the Enterprise Messaging service as a resource. I will briefly show you the important parts you have to add.

The Node module should be added under the “modules” section inside your “mta.yaml” file.

Extend your “ui5Websockets” module with the “NodeWsEm_api”.

Last but not least the Enterprise Messaging Service should be added as a resource under the respective “resources” section.

In the end your “mta.yaml” file should look like this:

ID: ui5Websockets
_schema-version: 3.2.0
description: Enter&nbspdescription&nbsphere
version: 0.0.1
parameters:
  enable-parallel-deployments: true

modules:
  - name: ui5Websockets
    type: nodejs
    path: approuter
    parameters:
      disk-quota: 512M
      memory: 512M
    requires:
      - name: ui5Websockets_destination
      - name: ui5Websockets_uaa
      - name: ui5Websockets_html5_repo_runtime
      - name: NodeWsEm_api
        group: destinations
        properties:
          forwardAuthToken: true
          name: NodeWsEm_api
          strictSSL: false
          url: "~{url}"
  - name: ui5Websockets_deployer
    type: com.sap.html5.application-content
    path: deployer
    requires:
      - name: ui5Websockets_html5_repo_host
    build-parameters:
      builder: custom
      commands:
        - npm&nbsprun&nbspbuild:ui --prefix ..
  - name: NodeWsEm
    type: nodejs
    path: NodeWsEm
    provides:
      - name: NodeWsEm_api
        properties:
          url: ${default-url}
    requires:
      - name: EnterpriseMessaging

resources:
  - name: ui5Websockets_destination
    type: org.cloudfoundry.managed-service
    parameters:
      service-plan: lite
      service: destination
  - name: ui5Websockets_uaa
    type: org.cloudfoundry.managed-service
    parameters:
      path: ./xs-security.json
      service-plan: application
      service: xsuaa
  - name: ui5Websockets_html5_repo_runtime
    type: org.cloudfoundry.managed-service
    parameters:
      service-plan: app-runtime
      service: html5-apps-repo
  - name: ui5Websockets_html5_repo_host
    type: org.cloudfoundry.managed-service
    parameters:
      service-plan: app-host
      service: html5-apps-repo
      config:
        sizeLimit: 5
  - name: EnterpriseMessaging
    type: org.cloudfoundry.existing-service
    parameters:
      service: enterprise-messaging
      service-plan: dev

The next change will occur in the “xs-app.json“ file inside your “approuter” directory.

Add the following routes to it and enable the WebSocket’s.

{
  "welcomeFile": "blogsui5Websockets/",
  "authenticationMethod": "route",
  "websockets": {
    "enabled": true
  },
  "logout": {
    "logoutEndpoint": "/do/logout"
  },
  "routes": [
    {
      "source": "^/NodeWS(.*)$",
      "target": "$1",
      "authenticationType": "none",
      "destination": "NodeWsEm_api",
      "csrfProtection": false
    },
    {
      "source": "^(.*)$",
      "target": "$1",
      "service": "html5-apps-repo-rt",
      "authenticationType": "xsuaa"
    }
  ]
}

The last change can be found in the “Error.controller.js” file inside your “webapp/controller” directory.

This code should look like this:

sap.ui.define([
  "blogs/ui5Websockets/controller/BaseController",
  "sap/ui/core/ws/WebSocket",
  "sap/m/MessageToast"
], function (Controller, WebSocket, MessageToast) {
  "use strict";

  return Controller.extend("blogs.ui5Websockets.controller.Error", {
    onInit: function () {
      var connection = new WebSocket("/NodeWS");
      // connection opened
      connection.attachOpen(function (oControlEvent) {
        console.log(oControlEvent.getParameter("data"));
      });

      // server messages
      connection.attachMessage(function (oControlEvent) {
        var msg = JSON.parse(oControlEvent.getParameter("data"));
        console.log(JSON.stringify(msg));
        MessageToast.show(msg.message || msg.errorMessage);
      });

      // error handling
      connection.attachError(function (oControlEvent) {
        console.log(oControlEvent.getParameter("data"));
      });

      // onConnectionClose
      connection.attachClose(function (oControlEvent) {
        console.log(oControlEvent.getParameter("data"));
      });
    }
  });
});

The “WebSocket” is imported as well as the “MessageToast” control.

The connection to the Node WebSocket is set via the path declared in de “xs-app.json” file inside your “approuter” directory. Next the different WebSocket scenarios are covered via the “attachOpen”, Message, Error and Close scenarios.

In the “attacheMessage” function the incoming message is parsed and displayed on the screen via a MessageToast. Either the “Hi” message of the server (WebSocket) or the error message from Postman or CPI is displayed.

With this you finished the full development and setup for your MTA Enterprise Messaging UI5 WebSocket application. Now it is time to deploy it.

Go to your terminal and navigate to the root directory of your MTA project.

Make sure you are logged in to Cloud Foundry and you are targeting the right organization and space.

Once double checked execute the deployment command:

npm run deploy

This will start the deployment and can be followed in the terminal window,

Once the deployment is finished you can go to your Cloud Platform account under your organization and space and you will see the applications were started under the “Applications” section.

Select the “ui5Websockets” application and open the URL.

You will see that the WebSocket server will greet you, just like we taught him to.

When you resend a message to the “ErrorQueue” via Postman or via CPI (AMQP) you will see that the Node application detected this message and that it is automatically shown inside the app because of the WebSocket connection.

Ep11. – Max’s Adventure in SAP Cloud Platform: Enterprise Messaging w/ Dries Van Vaerenbergh

For more information or a recap you can replay the Livestream “Ep11. – Max’s Adventure in SAP Cloud Platform: Enterprise Messaging w/ Dries Van Vaerenbergh” where Max and I have a deeper look on the Enterprise Messaging Service itself. This combined with a small chat WebSocket app. Enjoy!

 

Wrap up 🎁

With this you created a Multi Target application containing a Node.js app, which is responsible to connect to the Enterprise Messaging Service and will notify the UI5 application in real time with the appropriate messages.

The connection to the WebSocket could be place in the “Component.js” file or even in an “App.controller.js” file. In this blog I only showed you on how to connect the whole architecture.

Do notice, there is no authentication method implemented on your Node WebSocket application. This could be achieved by using the xsuaa service and the passport package. At the moment the “authenticationType” for the “NodeWS” path inside your “xs-app.json” file is also set to “none”. It should only take some small configurations and development to setup this security. Just do not forget it. 😉

I hope you found this blog interesting and that it may serve you in the future during your Enterprise Messaging, Node.js, Websocket and UI5 development.

Kind regards,

Dries

 

4 Comments
You must be Logged on to comment or reply to a post.