I am trying to implement server, as java plain application, where clients could connect to a web socket.
The goal is to connect client to a websocket via this url: ws://localhost:4550/api/myWebSocket
, and have some output from MyWebSocketHandler.
This is App class:
package com.main;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
public class App {
public static void main(String[] args) {
Server server = new Server(4550);
ServletContextHandler context = new
ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/api");
server.setHandler(context);
ServletHolder wsHolder = new ServletHolder(MyWebSocketServlet.class);
context.addServlet(wsHolder, "/");
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
} finally {
server.destroy();
}
}
}
This is MyWebSocketServlet class:
package com.main;
import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
import jakarta.servlet.annotation.WebServlet;
@WebServlet(urlPatterns = "/myWebSocket")
public class MyWebSocketServlet extends JettyWebSocketServlet {
@Override
protected void configure(JettyWebSocketServletFactory factory) {
factory.register(MyWebSocketHandler.class);
}
}
And lastly MyWebSocketHandler class:
package com.main;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
@WebSocket
public class MyWebSocketHandler {
@OnWebSocketConnect
public void onConnect(Session session) {
System.out.println("Connected: " + session.getRemoteAddress());
}
@OnWebSocketMessage
public void onMessage(Session session, String message) {
System.out.println("Message received: " + message);
try {
session.getRemote().sendString("Echo: " + message);
} catch (Exception e) {
e.printStackTrace();
}
}
@OnWebSocketClose
public void onClose(Session session, int statusCode, String reason) {
System.out.println("Closed: " + reason);
}
}
In gradle build file the following libraries are imported:
implementation 'org.eclipse.jetty:jetty-server:11.0.7'
implementation 'org.eclipse.jetty:jetty-servlet:11.0.7'
implementation 'org.eclipse.jetty.websocket:websocket-jetty-server:11.0.7'
It is required to be Java Application (no Spring Boot or other framework) and (if possible) Jetty library for defining the WebSocket.
ws-server
├── pom.xml
├── build.gradle
├── build.gradle.kts
└── src
└── main
└── java
└── com
└── example
└── wsserver
├── HelloWebSocketServerEndpoint.java
└── Main.java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>websocket-server-jakarta-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Jetty Examples :: Jetty 11.0.x :: Embedded :: WebSocket Server with Jakarta EE API</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<jetty.version>11.0.24</jetty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-jakarta-server</artifactId>
<version>${jetty.version}</version>
</dependency>
</dependencies>
<build>
<finalName>websocket-server</finalName>
</build>
</project>
Choose one between build.gradle and build.gradle.kts.
plugins {
id 'java'
id 'application'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
targetCompatibility = '17'
mainClassName = 'com.example.wsserver.Main'
ext {
jettyVersion = '11.0.24'
}
repositories {
mavenCentral()
}
dependencies {
implementation "org.eclipse.jetty:jetty-server:$jettyVersion"
implementation "org.eclipse.jetty:jetty-servlet:$jettyVersion"
implementation "org.eclipse.jetty:jetty-slf4j-impl:$jettyVersion"
implementation "org.eclipse.jetty.websocket:websocket-jakarta-server:$jettyVersion"
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
jar {
archiveFileName = "websocket-server.jar"
}
Choose one between build.gradle and build.gradle.kts.
plugins {
java
application
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
application {
mainClass.set("com.example.wsserver.Main")
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
val jettyVersion = "11.0.24"
repositories {
mavenCentral()
}
dependencies {
implementation("org.eclipse.jetty:jetty-server:$jettyVersion")
implementation("org.eclipse.jetty:jetty-servlet:$jettyVersion")
implementation("org.eclipse.jetty:jetty-slf4j-impl:$jettyVersion")
implementation("org.eclipse.jetty.websocket:websocket-jakarta-server:$jettyVersion")
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}
tasks.jar {
archiveFileName.set("websocket-server.jar")
}
package com.example.wsserver;
import jakarta.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main
{
private static final Logger LOG = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) throws Exception
{
Server server = newServer(4550);
LOG.info("new Server 4550");
server.start();
LOG.info("server.start()");
server.join();
LOG.info("server.join()");
}
public static Server newServer(int port)
{
Server server = new Server(port);
ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContextHandler.setContextPath("/api");
server.setHandler(servletContextHandler);
// Add jakarta.websocket support
JakartaWebSocketServletContainerInitializer.configure(servletContextHandler, (context, container) ->
{
// Add echo endpoint to server container
ServerEndpointConfig echoConfig = ServerEndpointConfig.Builder.create(HelloWebSocketServerEndpoint.class, "/myWebSocket").build();
container.addEndpoint(echoConfig);
});
return server;
}
}
package com.example.wsserver;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ServerEndpoint("/websocket")
public class HelloWebSocketServerEndpoint {
private static final Logger LOG = LoggerFactory.getLogger(HelloWebSocketServerEndpoint.class);
// Stores all connected client sessions
private static final CopyOnWriteArraySet<Session> sessions = new CopyOnWriteArraySet<>();
// Called when a new client connects
@OnOpen
public void onOpen(Session session) {
sessions.add(session);
LOG.info("WebSocket opened: " + session.getId());
broadcastMessage("Client " + session.getId() + " joined!");
}
// Called when a client message is received
@OnMessage
public void onMessage(String message, Session session) {
LOG.info("Message received from " + session.getId() +": " + message);
try {
session.getBasicRemote().sendText("Echo: " + message);
} catch (IOException e) {
LOG.warn("IOException: {}",e);
}
}
// Called when the client disconnects
@OnClose
public void onClose(Session session, CloseReason reason) {
sessions.remove(session);
LOG.info("WebSocket closed: " + session.getId());
broadcastMessage("Client " + session.getId() + " left.");
}
// Called when an error occurs
@OnError
public void onError(Session session, Throwable throwable) {
LOG.error("WebSocket error: " + session.getId());
throwable.printStackTrace();
}
// Broadcast a message to all clients
private void broadcastMessage(String message) {
LOG.info("broadcast >>>>> {}",message);
for (Session session : sessions) {
try {
session.getBasicRemote().sendText("broadcast >>>>> "+message);
} catch (IOException e) {
LOG.warn("IOException: {}",e);
}
}
}
}
Linux Command:
mvn clean package
Download dependencies jar files into target/libs
mvn dependency:copy-dependencies -DoutputDirectory=target/libs
java -cp "target/libs/*:target/websocket-server.jar" com.example.wsserver.Main
Linux Command:
gradle clean build
unzip distributions zip
cd build/distributions
unzip ws-server-0.0.1-SNAPSHOT.zip
Run
# build/distributions/ws-server-0.0.1-SNAPSHOT/bin
cd ws-server-0.0.1-SNAPSHOT/bin
./ws-server
ws-client
├── pom.xml
├── build.gradle
├── build.gradle.kts
└── src
└── main
└── java
└── com
└── example
└── wsclient
├── HelloWebsocketClientEndpoint.java
└── Main.java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>websocket-client-jakarta-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Jetty Examples :: Jetty 11.0.x :: Embedded :: WebSocket Client with Jakarta EE API</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<jetty.version>11.0.24</jetty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-jakarta-client</artifactId>
<version>${jetty.version}</version>
</dependency>
</dependencies>
<build>
<finalName>websocket-client</finalName>
</build>
</project>
Choose one between build.gradle and build.gradle.kts.
plugins {
id 'java'
id 'application'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
mainClassName = 'com.example.wsclient.Main'
repositories {
mavenCentral()
}
ext {
jettyVersion = '11.0.24'
}
dependencies {
implementation "org.eclipse.jetty:jetty-slf4j-impl:$jettyVersion"
implementation "org.eclipse.jetty.websocket:websocket-jakarta-client:$jettyVersion"
}
jar {
archiveBaseName.set("websocket-client")
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
Choose one between build.gradle and build.gradle.kts.
plugins {
java
application
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
application {
mainClass.set("com.example.wsclient.Main")
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
val jettyVersion = "11.0.24"
dependencies {
implementation("org.eclipse.jetty:jetty-slf4j-impl:$jettyVersion")
implementation("org.eclipse.jetty.websocket:websocket-jakarta-client:$jettyVersion")
}
tasks.jar {
archiveBaseName.set("websocket-client")
}
tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
}
package com.example.wsclient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger LOG = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
String serverUri = "ws://localhost:4550/api/myWebSocket";
LOG.info("serverUri: {}", serverUri);
HelloWebsocketClientEndpoint client = new HelloWebsocketClientEndpoint(serverUri);
// Send a message to the server
client.sendMessage("Hello from client at " + java.util.Calendar.getInstance().getTime() );
// Wait 5 seconds for a response
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
//e.printStackTrace();
LOG.warn("exception: {}",e);
}
// Close the client
client.close();
LOG.warn("client.close()");
}
}
package com.example.wsclient;
import jakarta.websocket.*;
import java.net.URI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ClientEndpoint
public class HelloWebsocketClientEndpoint {
private static final Logger LOG = LoggerFactory.getLogger(HelloWebsocketClientEndpoint.class);
private Session session;
public HelloWebsocketClientEndpoint(String serverUri) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
session = container.connectToServer(this, new URI(serverUri));
} catch (Exception e) {
LOG.warn("exception: {}",e);
}
}
@OnMessage
public void onMessage(String message) {
LOG.info("Received from server: {}" , message);
}
public void sendMessage(String message) {
LOG.info("sendMessage: {}" , message);
try {
if (session != null && session.isOpen()) {
session.getAsyncRemote().sendText(message);
} else {
LOG.info("Session is not open");
}
} catch (Exception e) {
LOG.warn("exception: {}",e);
}
}
public void close() {
try {
if (session != null) {
session.close();
LOG.info("close()");
}
} catch (Exception e) {
LOG.warn("exception: {}",e);
}
}
}
Linux Command:
mvn clean package
Download dependencies jar files into target/libs
mvn dependency:copy-dependencies -DoutputDirectory=target/libs
Execute 10 WebSocketClient connections to WebSocketServer simultaneously.
for i in {1..10}; do
java -cp "target/libs/*:target/websocket-client.jar" com.example.wsclient.Main &
done
Execute 1 WebSocketClient connections to WebSocketServer.
java -cp "target/libs/*:target/websocket-client.jar" com.example.wsclient.Main
Linux Command:
gradle clean build
unzip distributions zip
cd build/distributions
unzip ws-client-0.0.1-SNAPSHOT.zip
Run
# build/distributions/ws-client-0.0.1-SNAPSHOT/bin
cd ws-client-0.0.1-SNAPSHOT/bin
./ws-client
Execute 10 WebSocketClient connections to WebSocketServer simultaneously.
for i in {1..10}; do
./ws-client &
done
Jetty websocket examples have 2 examples:
This answer is based on websocket-jakarta-api.
https://github.com/jetty/jetty-examples/blob/11.0.x/embedded/websocket-jakarta-api/pom.xml