DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Full-Duplex Scalable Client-Server Communication with WebSockets and Spring Boot (Part I)
  • Java Is Greener on Arm
  • Mastering Date/Time APIs: Challenges With Java's Calendar and JDK Date/Time APIs
  • Java 23: What Developers Need to Know

Trending

  • The Evolution of Scalable and Resilient Container Infrastructure
  • Using Java Stream Gatherers To Improve Stateful Operations
  • Implementing Explainable AI in CRM Using Stream Processing
  • How To Build Resilient Microservices Using Circuit Breakers and Retries: A Developer’s Guide To Surviving
  1. DZone
  2. Coding
  3. Java
  4. Jakarta WebSocket Essentials: A Guide to Full-Duplex Communication in Java

Jakarta WebSocket Essentials: A Guide to Full-Duplex Communication in Java

Do you want to explore real-time apps in Jakarta EE? This article covers the basics of building a real-time chat app using Jakarta WebSocket and Open Liberty.

By 
Sweetty P Devassy user avatar
Sweetty P Devassy
·
Abhinav K user avatar
Abhinav K
·
Nov. 05, 24 · Tutorial
Likes (15)
Comment
Save
Tweet
Share
16.5K Views

Join the DZone community and get the full member experience.

Join For Free

Have you ever wondered what happens when you send a message to friends or family over the Internet? It’s not just magic — there’s a fascinating technology at work behind the scenes called WebSocket. This powerful protocol enables real-time communication, allowing messages to flow seamlessly between users.

Join us as we dive deeper into the world of WebSocket! We’ll explore how this technology operates and even create a simple application together to see it in action. Get ready to unlock the potential of real-time communication!

What Is a WebSocket?

WebSocket is a communication protocol that provides full-duplex communication channels over a single, long-lived connection, which means we can transfer data in both directions simultaneously. Unlike traditional HTTP requests, where a client sends a request to a server and waits for a response, WebSocket allows both the client and server to send and receive messages independently and concurrently. This is achieved through a persistent connection that remains open for real-time data exchange.

For this blog, we are going to use Jakarta, the enterprise edition of Java, to implement WebSocket. Before we dive deeper into WebSocket, let's take a look at Jakarta EE.

What Is Jakarta EE?

Jakarta EE (formerly Java EE) is a set of specifications that extend the Java SE (Standard Edition) with a collection of APIs for building enterprise-grade applications. It provides a robust framework for developing scalable, reliable, and secure applications that can run on various servers and cloud platforms.

WebSockets in Jakarta

WebSockets in Jakarta EE offer a powerful and efficient way to enable real-time communication between clients and servers. Jakarta EE's WebSocket API simplifies the process of building real-time applications by providing a robust framework for managing WebSocket connections. With its event-driven model and seamless integration with other Jakarta EE technologies, developers can create interactive, responsive applications that enhance user engagement and experience.

WebSocket Protocol

Let's jump back to WebSocket and learn about the WebSocket protocol. The WebSocket protocol is designed to provide full-duplex communication channels over a single TCP connection, making it ideal for real-time applications. Here are the key aspects and fundamentals of how the WebSocket protocol works:

1. Establishing a Connection

Handshake Process

The connection starts with a handshake initiated by the client through an HTTP request. This request includes specific headers indicating that the client wishes to establish a WebSocket connection.

Server Response

If the server supports WebSocket, it responds with a status code 101 (Switching Protocols) and confirms the upgrade.

2. Data Framing

Messages

After the connection is established, data is transmitted in the form of messages. Each message can be text (UTF-8) or binary data.

Frames

WebSocket messages are divided into frames. Each frame contains a header that includes information like whether the frame is a final frame or if it’s part of a larger message, as well as the payload length and masking information.

3. Message Types

  • Text frames: These frames contain UTF-8 encoded text messages.
  • Binary frames: Used for binary data, such as images or files
  • Control frames: These include frames for closing the connection and ping/pong frames for keep-alive functionality.

4. Closing the Connection

Close Frame

Either the client or server can initiate the closing of the WebSocket connection by sending a close frame, which includes a status code and an optional reason for the closure.

Acknowledgment

Upon receiving a close frame, the other party must respond with its own close frame to confirm the closure.

5. Keep-Alive Mechanism

Ping/pong frames: To maintain an active connection and check if the other party is still connected, WebSockets can use ping/pong frames. The server sends a ping frame, and the client responds with a pong frame, helping to keep the connection alive.

6. Security Considerations

Secure WebSockets (WSS): Similar to HTTPS for HTTP, WebSocket connections can be secured using the WSS protocol, which encrypts the data transmitted over the connection using TLS.

Setting Up Jakarta WebSocket

To better understand WebSocket, let's build a small app that implements WebSocket. In this project, we are going to use Open Liberty, which is an open-source Java application server designed to support enterprise Java applications developed by IBM.

Setting up Jakarta WebSocket on Open Liberty is straightforward, as Open Liberty supports Jakarta EE APIs, including Jakarta WebSocket.

1. Install JDK 11 or above.

2. Set the JAVA_HOME environment variable. For example:

  • Linux/macOS: export JAVA_HOME=/path/to/jdk
  • Windows: set JAVA_HOME=C:\path\to\jdk

3. Download Open Liberty:

  • Go to the Open Liberty starter page.
  • Choose the appropriate release and download the .zip file.

4. Extract the files:

  • Extract the downloaded .zip file to your preferred directory.

5. Update server.xml file in liberty/config with the WebSocket configuration:

XML
 
<featureManager>
  <feature>jakartaee-10.0</feature>
  <feature>microProfile-6.1</feature>
  <feature>webProfile-10.0</feature>
  <feature>websocket-2.1</feature>
</featureManager>


6. Create the WebSocket Java Class:

  • In src/main/java/com/openLibertyWebsocket/rest, create a class named ChatEndpoint.java:
Java
 
package com.openLibertyWebsocket.rest;

import jakarta.websocket.OnClose;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

@ServerEndpoint("/chat")
public class ChatEndpoint {

    // Maintain a set of all active WebSocket sessions
    private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<>());

    @OnOpen
    public void onOpen(Session session) {
        sessions.add(session);
        System.out.println("Connected: " + session.getId());
    }

    @OnMessage
    public void onMessage(String message, Session senderSession) {
        System.out.println("Received: " + message + " from " + senderSession.getId());

        // Broadcast the message to all connected clients
        synchronized (sessions) {
            for (Session session : sessions) {
                if (session.isOpen()) {
                    try {
                        session.getBasicRemote().sendText("User " + senderSession.getId() + ": " + message);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    @OnClose
    public void onClose(Session session) {
        sessions.remove(session);
        System.out.println("Disconnected: " + session.getId());
    }
}


This Java class, ChatEndpoint, defines a WebSocket server endpoint for a simple chat application using the Jakarta WebSocket API. This allows clients to connect to the /chat endpoint, send messages, and broadcast messages to all other connected clients.

  • Class Annotation @ServerEndpoint:
    • @ServerEndpoint("/chat") indicates that this class is a WebSocket endpoint at the URL /chat. When a WebSocket client connects to ws://<server-address>/chat, it interacts with this endpoint.
  • Session management with Set<Session>:
    • sessions: A static, synchronized Set of Session objects, representing all active WebSocket connections. The Collections.synchronizedSet(new HashSet<>()) ensures thread-safe access, allowing concurrent modification when clients connect or disconnect.
  • Methods for WebSocket Events: Each WebSocket lifecycle event has a corresponding method in this class, annotated appropriately to handle client connections, messages, and disconnections.
    • @OnOpen: The onOpenmethod is called when a client establishes a connection.
      • Adds the client’s Session to the sessions set, allowing the server to track active connections.
      • Prints a message to the console confirming the client’s connection with its unique session ID.
    • @OnMessage: The onMessagemethod is called whenever a connected client sends a message to the server.
      • Receives the message as a String and the Session of the sender (senderSession).
      • Logs the received message and the sender’s session ID for debugging.
      • Broadcasting: Uses a synchronized loop to send the message to all active clients in the sessions set:
        • If the session is open, the sendText method sends the message to that client.
        • Adds a prefix (e.g., User <session-id>) to identify the sender in the message broadcast to all clients.
    • @OnClose: The onClosemethod is called when a client disconnects.
      • Removes the client’s Session from the sessions set.
      • Logs a disconnect message with the session ID, helpful for tracking connections and debugging.

The entire backend code is available in this repository.

7. Run the project using Maven: mvn liberty:dev

8. Test the chat application.

  • Open two browser windows.
  • Use the browser console as the WebSocket client (in Chrome, press F12 > Console tab).
  • Connect each tab to the WebSocket server by running the following JavaScript.
JavaScript
 
const ws = new WebSocket("ws://localhost:9080/ws-blog/chat");

ws.onopen = () => console.log("Connected to chat");

ws.onmessage = (msg) => console.log("Message from server: " + msg.data);

ws.onclose = () => console.log("Disconnected from chat");



// Send a message to the chat

function sendMessage(text) {

    ws.send(text);

}

// Example: To send a message, type sendMessage("Hello from user!");


9. Start chatting:

  • In each console, use sendMessage("Your message") to send messages.
  • You should see messages from both users appearing in both browser consoles, creating a live chat experience.

10. After testing the WebSocket application, we can proceed to create a chat user interface using React.js. Here is the sample code that we have used to create one.

import React, { useEffect, useState, useRef } from 'react';

const Chat = () => {
    const ws = useRef(null); // Use useRef to persist WebSocket instance
    const [messages, setMessages] = useState([]);
    const [input, setInput] = useState('');
    const [username, setUsername] = useState('');
    const [isUsernameSet, setIsUsernameSet] = useState(false);

    useEffect(() => {
        // Initialize WebSocket connection only once
        ws.current = new WebSocket('ws://localhost:9080/ws-blog/chat');

        // Handle incoming messages
        ws.current.onmessage = (event) => {
            const newMessage = event.data;

            // Remove random prefix if present
            const displayMessage = newMessage.replace(/User\s[\w-]+:\s/, '');

            setMessages((prevMessages) => [...prevMessages, displayMessage]);
        };

        // Clean up WebSocket on component unmount
        return () => {
            if (ws.current) {
                ws.current.close();
            }
        };
    }, []);

    // Function to send a message with the specified username
    const sendMessage = () => {
        if (ws.current && ws.current.readyState === WebSocket.OPEN && input) {
            const messageToSend = `${username}: ${input}`; // Use specified username
            ws.current.send(messageToSend);
            setInput(''); // Clear the input field
        }
    };

    // Set the specified username
    const handleSetUsername = () => {
        if (username.trim()) {
            setIsUsernameSet(true);
        }
    };

    return (
        <div>
            <h1>WebSocket Chat</h1>

            {!isUsernameSet ? (
                // Username input screen
                <div>
                    <input 
                        type="text" 
                        value={username} 
                        onChange={(e) => setUsername(e.target.value)} 
                        placeholder="Enter your username"
                    />
                    <button onClick={handleSetUsername}>Start Chat</button>
                </div>
            ) : (
                // Chat interface
                <div>
                    <div style={{ border: '1px solid #ccc', height: '300px', overflowY: 'scroll', marginBottom: '10px' }}>
                        {messages.map((msg, index) => (
                            <div key={index}>{msg}</div>
                        ))}
                    </div>
                    <input 
                        type="text" 
                        value={input} 
                        onChange={(e) => setInput(e.target.value)} 
                        placeholder="Type a message..."
                    />
                    <button onClick={sendMessage}>Send</button>
                </div>
            )}
        </div>
    );
};

export default Chat;


React Using WebSockets

This code defines a basic chat interface in React using WebSockets for real-time communication. The Chat component allows a user to set a username, send messages to a WebSocket server, and receive/display incoming messages.

1. WebSocket Reference (ws)

ws is defined with useRef(null) to maintain a persistent reference to the WebSocket instance across renders.

2. useEffect Hook for WebSocket Connection

useEffect initializes the WebSocket connection to ws://localhost:9080/ws-blog/chat when the component mounts. This is only run once due to the empty dependency array ([]).

  • Message handling: When a message is received, it triggers the onmessageevent, where:
    • newMessage captures the received message text.
    • displayMessage uses a regex to strip any unwanted random user prefix (in the format User <random-identifier>: ), displaying only the intended message content.
    • setMessages updates the messages array with the new message.
  • Cleanup: When the component unmounts, the WebSocket connection is closed to free up resources.

3. sendMessage Function

  • Called when the user clicks the "Send" button
  • Checks if the WebSocket connection is open and the input field has text
  • Formats the message with the username and input content, then sends it to the server
  • Clears the input field

4. handleSetUsername Function

  • Called when the user sets a username and clicks "Start Chat"
  • If a non-empty username is entered, isUsernameSet is set to true, hiding the username input and displaying the chat interface.

5. UI Rendering

  • If isUsernameSet is false, the component displays an input to enter a username and a "Start Chat" button.
  • Once the username is set, the main chat interface is shown:
    • Messages display: A scrollable div shows each message in the messages array.
    • Message input: An input field and "Send" button allow users to type and send messages.

The entire frontend code is available in this repository.

Conclusion

As we wrap up, you've now unlocked the basics of WebSockets and learned how to create a simple WebSocket application with Open Liberty. Here’s a quick recap of what we covered:

1. Understanding WebSockets

We explored the WebSocket protocol, which enables real-time, bidirectional communication between clients and servers. Unlike traditional HTTP requests, WebSockets maintain a persistent connection that allows data to flow freely in both directions.

2. Setting Up Open Liberty

You learned how to set up your Open Liberty environment and create a basic Jakarta EE application that uses WebSockets. We covered the necessary steps to prepare your project structure and configure the server.

3. Creating a WebSocket Endpoint

We walked through the creation of a WebSocket endpoint using the @ServerEndpoint annotation. This included writing the server-side logic to handle client connections, messages, and disconnections.

4. Building the Client Application

You gained insights into how to build a simple client application that connects to your WebSocket server. We discussed the JavaScript code necessary to establish a connection, send messages, and receive updates in real time.


By mastering these fundamental concepts, you now have the tools to build interactive and dynamic applications that can engage users in real-time. WebSockets are a powerful feature of modern web development, and with your new knowledge, you can start creating applications that leverage their capabilities.

Java Development Kit WebSocket Connection (dance) Frame (networking) Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Full-Duplex Scalable Client-Server Communication with WebSockets and Spring Boot (Part I)
  • Java Is Greener on Arm
  • Mastering Date/Time APIs: Challenges With Java's Calendar and JDK Date/Time APIs
  • Java 23: What Developers Need to Know

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!