Biggy Poopa
Biggy Poopa

Reputation: 99

Cannot connect to Jetty WebSocket v11

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.

Upvotes: 0

Views: 52

Answers (1)

life888888
life888888

Reputation: 3129

ws-server

Project Tree

ws-server
├── pom.xml
├── build.gradle
├── build.gradle.kts
└── src
    └── main
        └── java
            └── com
                └── example
                    └── wsserver
                        ├── HelloWebSocketServerEndpoint.java
                        └── Main.java

pom.xml

<?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>

build.gradle

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"
}

build.gradle.kts

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")
}

Main.java

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;
    }
}

HelloWebSocketServerEndpoint.java

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);
            }
        }
    }
}

Maven - Build and Run

Linux Command:

Build

mvn clean package

Download dependencies jars

Download dependencies jar files into target/libs

mvn dependency:copy-dependencies -DoutputDirectory=target/libs

Run

java -cp "target/libs/*:target/websocket-server.jar" com.example.wsserver.Main

Gradle - Build and Run

Linux Command:

Build

gradle clean build

Run

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

Project Tree

ws-client
├── pom.xml
├── build.gradle
├── build.gradle.kts
└── src
    └── main
        └── java
            └── com
                └── example
                    └── wsclient
                        ├── HelloWebsocketClientEndpoint.java
                        └── Main.java

pom.xml

<?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>

build.gradle

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'
}

build.gradle.kts

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"
}

Main.java

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()");
    }

}

HelloWebSocketServerEndpoint.java

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);
        }

    }

}

Maven - Build and Run

Linux Command:

Build

mvn clean package

Download dependencies jars

Download dependencies jar files into target/libs

mvn dependency:copy-dependencies -DoutputDirectory=target/libs

Run

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

Gradle - Build and Run

Linux Command:

Build

gradle clean build

Run

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

Note

Jetty websocket examples have 2 examples:

  • websocket-jakarta-api
  • websocket-jetty-api

This answer is based on websocket-jakarta-api.

https://github.com/jetty/jetty-examples/blob/11.0.x/embedded/websocket-jakarta-api/pom.xml

Upvotes: 1

Related Questions