Have you ever thought about what the logic behind a chat application is? How could it be written and how does it work? If yes, here is the right place to look for an answer. It is called WebSocket
! But what is this?
What is WebSocket?
WebSocket
is a communication protocol exactly like HTTP
providing a full duplex communication channel between server and client. The principle is that once a WebSocket
connection between a server and a client exists, they can communicate and exchange information until one of them (the server or the client) disconnects. If there is the need for a communication and information exchange at high frequency and low latency, WebSocket
should be preferred over HTTP
. The WebSocket
process is the following:
- First of all, the client establishes a normal
HTTP
connection with the server. - Then it transforms the connection into a full duplex communication channel.
- The step above is done by sending an
Upgrade
header which communicates to the server that the client wants to establish aWebSocket
connection.
With Java 11
With the Java 11 HttpClient
class, Java applications can not only execute synchronous or asynchronous HTTP
requests. This library also supports the WebSocket
protocol.
Well, Java 11 has a standardized HTTP
client capable of WebSocket
connections!
The example presented below is a chat application using HttpClient
‘s WebSocket
capabilities using Spring Boot for the server side application.
Let’s give it a try
We create a Spring Boot application, which helps us with the server side. The Spring Boot starter dependency for WebSocket
is necessary for the application. I added it directly from my IDE. If you cannot do this, you can as well use the Spring Initializer web page to create a project with the necessary dependencies.

In Gradle
this dependency declaration looks like this:
dependencies { implementation 'org.springframework.boot:spring-boot-starter-websocket' }
Once created, we can see the main class which was generated for us:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Let’s dive in with the developing part and of course the explanations:
We start with the ScheduledWebSocketHandler
class which inherits from TextWebSocketHandler
to process text messages via WebSocket
:
public class ScheduledWebSocketHandler extends TextWebSocketHandler{ ... }
First, we define the active sessions list using the CopyOnWriteArrayList
like this:
private List<WebSocketSession> activeSessions = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList
from the java.util.concurrent
package is the enhanced version of ArrayList
where all updates like add
, set
, remove
are implemented by making a fresh copy of the underlying array.
Because our class inherits from the TextWebSocketHandler
we can implement the three necessary methods:
void handleTextMessage(WebSocketSession session, TextMessage message)
void afterConnectionEstablished(WebSocketSession session)
void afterConnectionClosed(WebSocketSession session, CloseStatus status)
I will first focus on the first one (handleTextMessage
): Here the message will be sent to all sessions except the current session. We iterate through all active sessions and send the message.
When a new session is created, the afterConnectionEstablished
method is called in which new active session are added to the active sessions list (CopyOnWriteArrayList)
. When the connection is closed, the respective session will be removed from the active session list in the method afterConnectionClosed
.
In the end, our ScheduledWebSocketHandler
class will look like this:
public class ScheduledWebSocketHandler extends TextWebSocketHandler {
private List<WebSocketSession> activeSessions = new CopyOnWriteArrayList<>();
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
activeSessions.forEach(session -> {
try {
if (!session.equals(session)) {
session.sendMessage(message);
System.out.println("Message from Client received: " + message);
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
activeSessions.add(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
activeSessions.remove(session);
}
}
This was all about the server side. Now let’s move on to the client side:
First, we define the httpClient
and then we create a WebSocket
builder using the HttpClient
and the newWebSocketBuilder()
method. After the request is sent asynchronously, the return value will of course be a CompletableFuture
.
After the data has been received, the WebSocket
invokes a receive method. The method onText(...)
returns a CompletionStage
which is completed when the message has been received by the listener. In this case, it prints out the message the server received from the client. You can find out more about the HttpClient
synchronous and asynchronous mechanisms by reading the first two blog posts of the HTTP client blog posts series (starting here).
But now let’s turn back to our subject. When a session has started, the message “WebSocket Client connected” is shown. The user can type in a message which will be sent to the server which in turn will forward it to the other active sessions. If one of the sessions sends the word “quit”, then the respective session will be closed automatically.
The code for this is as follows:
public class WebSocketMain {
@SuppressWarnings("resource")
public static void main(String[] args) throws InterruptedException {
Executor executor = ForkJoinPool.commonPool();
HttpClient httpClient = HttpClient.newBuilder().executor(executor).build();
Builder webSocketBuilder = httpClient.newWebSocketBuilder();
CompletableFuture<WebSocket> webSocketFuture = webSocketBuilder
.buildAsync(URI.create("ws://localhost:8081/messages"), new WebSocket.Listener() {
@Override
public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
System.out.println("Message received from Server:"+" " + data);
return Listener.super.onText(webSocket, data, last);
}
});
WebSocket webSocket = webSocketFuture.join();
System.out.println("WebSocket Client connected");
String message;
Scanner scanner = new Scanner(System.in);
System.out.println("Write your message: ");
do {
message = scanner.nextLine();
webSocket.sendText(message, true);
} while (!message.equalsIgnoreCase("quit"));
CompletableFuture<WebSocket> sendClose = webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok");
sendClose.thenRun(() -> System.out.println("Connection will be closed"));
}
}
Congratulations, this was the last step. Now let’s see a demo of it to prove that the magic is done:
- First of all we start our server by running the Spring Boot application.
- Then we run the
WebSocketMain
as a Java application at least two times to have at least two active sessions. Please scroll through your code one more time to check if the ports are the same. Normally the used port is 8080 but in my case the used port is 8081. You can check this in the main class in theWebSocket
builder URL: “ws://localhost:8081/messages”. If you want to use the same port as me on the server side you should enter the following line of code in theapplication.properties
file:server.port = 8081
. After the application has started, the following will show up:

Now let’s start chatting. The message from the first active session:

The message from the second active session:

This is the message the server will send to the first active session:

By writing the “quit” message, the connection will be closed as shown in the image below:

While all the above is done the following info is sent to the server:

I hope you enjoyed this demo and learned something from it. This article is part of the HTTP client blog post series. The HTTP client also supports push promises which will be the subject for the next blog post. Another demo will be explained step by step. So stay tuned.