Skip to content

A guide on how to use Jakarta WebSocket to send and receive messages between services without closing the connection.

License

Notifications You must be signed in to change notification settings

OpenLiberty/guide-jakarta-websocket

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Bidirectional communication between services using Jakarta WebSocket

Note
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website.

Learn how to use Jakarta WebSocket to send and receive messages between services without closing the connection.

What you’ll learn

Jakarta WebSocket enables two-way communication between client and server endpoints. First, each client makes an HTTP connection to a Jakarta WebSocket server. The server can then broadcast messages to the clients. Server-Sent Events (SSE) also enables a client to receive automatic updates from a server via an HTTP connection however WebSocket differs from Server-Sent Events in that SSE is unidirectional from server to client, whereas WebSocket is bidirectional. WebSocket also enables real-time updates over a smaller bandwidth than SSE. The connection isn’t closed meaning that the client can continue to send and receive messages with the server, without having to poll the server to receive any replies.

The application that you will build in this guide consists of the client service and the system server service. The following diagram depicts the application that is used in this guide.

Application architecture where system and client services use the Jakarta Websocket API to connect and communicate.

You’ll learn how to use the Jakarta WebSocket API to build the system service and the scheduler in the client service. The scheduler pushes messages to the system service every 10 seconds, then the system service broadcasts the messages to any connected clients. You will also learn how to use a JavaScript WebSocket object in an HTML file to build a WebSocket connection, subscribe to different events, and display the broadcasting messages from the system service in a table.

Try what you’ll build

The finish directory in the root of this guide contains the finished application. Give it a try before you proceed.

To try out the application, go to the finish directory and run the following Maven goal to build the system service and deploy it to Open Liberty:

mvn -pl system liberty:run

Next, open another command-line session and run the following command to start the client service:

mvn -pl client liberty:run

After you see the following message in both command-line sessions, both your services are ready.

The defaultServer is ready to run a smarter planet.

Check out the service at the http://localhost:9080 URL. See that the table is being updated for every 10 seconds.

After you are finished checking out the application, stop both the system and client services by pressing CTRL+C in the command-line sessions where you ran them. Alternatively, you can run the following goals from the finish directory in another command-line session:

mvn -pl system liberty:stop
mvn -pl client liberty:stop

Creating the WebSocket server service

In this section, you will create the system WebSocket server service that broadcasts messages to clients.

Navigate to the start directory to begin.

When you run Open Liberty in dev mode, dev mode listens for file changes and automatically recompiles and deploys your updates whenever you save a new change. Run the following command to start the system service in dev mode:

mvn -pl system liberty:dev

After you see the following message, your Liberty instance is ready in dev mode:

**************************************************
*     Liberty is running in dev mode.

The system service is responsible for handling the messages produced by the client scheduler, building system load messages, and forwarding them to clients.

Create the SystemService class.
system/src/main/java/io/openliberty/guides/system/SystemService.java

SystemService.java

link:finish/system/src/main/java/io/openliberty/guides/system/SystemService.java[role=include]

Annotate the SystemService class with a @ServerEndpoint annotation to make it a WebSocket server. The @ServerEndpoint value attribute specifies the URI where the endpoint will be deployed. The encoders attribute specifies the classes to encode messages and the decoders attribute specifies the classes to decode messages. Provide methods that define the parts of the WebSocket lifecycle like establishing a connection, receiving a message, and closing the connection by annotating them with the @OnOpen, @OnMessage and @OnClose annotations respectively. The method that is annotated with the @OnError annotation is responsible for tackling errors.

The onOpen() method stores up the client sessions. The onClose() method displays the reason for closing the connection and removes the closing session from the client sessions.

The onMessage() method is called when receiving a message through the option parameter. The option parameter signifies which message to construct, either system load, memory usage data, or both, and sends out the JsonObject message. The sendToAllSessions() method uses the WebSocket API to broadcast the message to all client sessions.

Create the SystemLoadEncoder class.
system/src/main/java/io/openliberty/guides/system/SystemLoadEncoder.java

SystemLoadEncoder.java

link:finish/system/src/main/java/io/openliberty/guides/system/SystemLoadEncoder.java[role=include]

The SystemLoadEncoder class implements the Encoder.Text interface. Override the encode() method that accepts the JsonObject message and converts the message to a string.

Create the SystemLoadDecoder class.
system/src/main/java/io/openliberty/guides/system/SystemLoadDecoder.java

SystemLoadDecoder.java

link:finish/system/src/main/java/io/openliberty/guides/system/SystemLoadDecoder.java[role=include]

The SystemLoadDecoder class implements the Decoder.Text interface. Override the decode() method that accepts string message and decodes the string back into a JsonObject. The willDecode() override method checks out whether the string can be decoded into a JSON object and returns a Boolean value.

system/server.xml

link:finish/system/src/main/liberty/config/server.xml[role=include]

The required websocket and jsonb features for the system service have been enabled for you in the Liberty server.xml configuration file.

Creating the client service

In this section, you will create the WebSocket client that communicates with the WebSocket server and the scheduler that uses the WebSocket client to send messages to the server. You’ll also create an HTML file that uses a JavaScript WebSocket object to build a WebSocket connection, subscribe to different events, and display the broadcasting messages from the system service in a table.

On another command-line session, navigate to the start directory and run the following goal to start the client service in dev mode:

mvn -pl client liberty:dev

After you see the following message, your Liberty instance is ready in dev mode:

**************************************************
*     Liberty is running in dev mode.
Create the SystemClient class.
client/src/main/java/io/openliberty/guides/client/scheduler/SystemClient.java

client/SystemClient.java

link:finish/client/src/main/java/io/openliberty/guides/client/scheduler/SystemClient.java[role=include]

Annotate the SystemClient class with @ClientEndpoint annotation to make it as a WebSocket client. Create a constructor that uses the websocket APIs to establish connection with the server. Provide a method with the @OnOpen annotation that persists the client session when the connection is established. The onMessage() method that is annotated with the @OnMessage annotation handles messages from the server.

Create the SystemLoadScheduler class.
client/src/main/java/io/openliberty/guides/client/scheduler/SystemLoadScheduler.java

SystemLoadScheduler.java

link:finish/client/src/main/java/io/openliberty/guides/client/scheduler/SystemLoadScheduler.java[role=include]

The SystemLoadScheduler class uses the SystemClient class to establish a connection to the server by the ws://localhost:9081/systemLoad URI at the @PostConstruct annotated method. The sendSystemLoad() method calls the client to send a random string from either cpuLoad, memoryUsage, or both to the system service. Using the Jakarta Enterprise Beans Timer Service, annotate the sendSystemLoad() method with the @Schedule annotation so that it sends out a message every 10 seconds.

Now, create the front-end UI. The images and styles for the UI are provided for you.

Create the index.html file.
client/src/main/webapp/index.html

index.html

link:finish/client/src/main/webapp/index.html[role=include]

The index.html front-end UI displays a table in which each row contains a time, system load, and the memory usage of the system service. Use a JavaScript WebSocket object to establish a connection to the server by the ws://localhost:9081/systemLoad URI. The webSocket.onopen event is triggered when the connection is established. The webSocket.onmessage event receives messages from the server and inserts a row with the data from the message into the table. The webSocket.onerror event defines how to tackle errors.

client/server.xml

link:finish/client/src/main/liberty/config/server.xml[role=include]

The required features for the client service are enabled for you in the Liberty server.xml configuration file.

Running the application

Because you are running the system and client services in dev mode, the changes that you made are automatically picked up. You’re now ready to check out your application in your browser.

Point your browser to the http://localhost:9080 URL to test out the client service. Notice that the table is updated every 10 seconds.

Visit the http://localhost:9080 URL again on a different tab or browser and verify that both sessions are updated every 10 seconds.

Testing the application

Create the SystemClient class.
system/src/test/java/it/io/openliberty/guides/system/SystemClient.java

system/test/SystemClient.java

link:finish/system/src/test/java/it/io/openliberty/guides/system/SystemClient.java[role=include]

The SystemClient class is used to communicate and test the system service. Its implementation is similar to the client class from the client service that you created in the previous section. At the onMessage() method, decode and verify the message.

Create the SystemServiceIT class.
system/src/test/java/it/io/openliberty/guides/system/SystemServiceIT.java

SystemServiceIT.java

link:finish/system/src/test/java/it/io/openliberty/guides/system/SystemServiceIT.java[role=include]

There are two test cases to ensure correct functionality of the system service. The testSystem() method verifies one client connection and the testSystemMultipleSessions() method verifies multiple client connections.

Running the tests

Because you started Open Liberty in dev mode, you can run the tests by pressing the enter/return key from the command-line session where you started the system service.

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.system.SystemServiceIT
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.247 s - in it.io.openliberty.guides.system.SystemServiceIT

Results:

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

When you are done checking out the services, exit dev mode by pressing CTRL+C in the command-line sessions where you ran the system and client services.

Great work! You’re done!

You developed an application that subscribes to real time updates by using Jakarta WebSocket and Open Liberty.