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
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 description here
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 run build: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
Really great work Dries. Good structured documents with easy to understand examples. Learned a lot
Thank you Helmut
Thanks a lot Helmut!
Really appreciate the feedback!
Happy to hear the examples are clear!
Again a big thanks!
Kind regards,
Dries
Hello Dries Van Vaerenbergh , thanks very much for this great post...! Very good that you're passionate about sharing and please go on with that 😉
Cheers,
Carlos
Hi Carlos Roggan thanks for the feedback!
Learned a lot from many Community members their blog posts, including yours! Thanks for that!
Best regards,
Dries
Hi, great Blog!
I am trying to rebuild what you did but I get stuck at the deployment "npm run deploy".
This is the Error I get:
Hi thank you for your feedback and nice that you are trying it out!
It looks like you are trying to develop the application locally on you computer, is that correct?
In case you are developing locally you have to make sure that you have the "Cloud MTA Build Tool (MBT)" installed in order to build to MTA project.
You can check if you have tis installed already by executing the following command:
mbt build
In case it is not installed you can download and follow the installation steps here:
https://sap.github.io/cloud-mta-build-tool/download/
I created this MTA project inside the SAP Business Application Studio which comes with different types of workspaces with all the required commands and modules preinstalled. I used the "SAP Cloud Business Application" Workspace.
I hope the above explanation addresses your problem and if not please let me know!
Thanks again for your reaction and feedback.
Good luck!
Kind regards,
Dries
Thanks for your great sharing Dries!
I am trying to rebuild what you did but I get stuck at the deployment “npm run deploy”.
So I use mbt build instead and I get a error message, can you give me some suggestion? Many thanks.
Hi Yao-Ching,
Thanks for your feedback! Nice you are trying it out yourself!
Regarding your error message:
Could you check those and adjust if needed?
I hope this helps already! 🙂
Best regards,
Dries
Hi,
Can I know the detail config in the destination NodeWsEm_api, should I use the wss://my-srv-socket.url one or just put the https://my-srv-socket.url? I'm having trouble to connect to the websocket through the AppRouter. I can connect directly through the websocket URL (https://my-srv-socket.url), but if I want to connect through the AppRouter path: const socket = io("/srv-socket"), it will not able to connect.
Hi Dries Van Vaerenbergh
I am getting upgrade required error while running the node application and the ui is also not rendering properly.Could you please help me with this.
Thank you
Regards,
Keerthana
Dear Dries Van Vaerenbergh,
I followed your blog. everything was fine for me but now i am getting AMQP not allowed error.
AmqpError: target (topic/queue) forbidden (220056) [amqp:not-allowed] [receiver: S11/S4HANA/SO/SalesOrder; sapGatewayErrorCode: V1/220056] error. few months ago it was working fine.
Any idea?
Regards
Naveen Jain