enhancement
major
minor
major
minor
minor
#29898
Development start (tl-ide-jetty) provides no Jakarta WebSocket container
TopLogic 8.0.0-alpha10, E-Home (haumacher/ehome #54, doc/controllers.md): the application registers a Jakarta WebSocket endpoint (javax/jakarta.websocket ServerEndpoint) for its controllers. The development start tl-ide-jetty Bootstrap (Jetty 12.1.14 ee10) runs no WebSocket implementation: there is no jakarta.websocket.server.ServerContainer attribute in the servlet context and no annotation scanning that would run the initializer of an implementation on the classpath. The servlet API's own HTTP upgrade is no substitute: in Jetty 12.1.14 the upgraded request keeps its end-of-content mark, so a request without a body (every WebSocket handshake) reads only its end after the upgrade and the next bytes are parsed as a new HTTP request (HttpConnection.HttpStreamOverHTTP1#read; an upstream Jetty issue). Workaround in E-Home: jetty-ee10-websocket-jakarta-server as a test/exec dependency (not in the war) and a web.xml listener that runs Jetty's initializer by name when the container has no ServerContainer. Expected: the development start offers the Jakarta WebSocket API like Tomcat and a Jetty with the websocket module do, so applications can declare endpoints portably.
Lösung
The development start provides the Jakarta WebSocket container; registering the endpoints stays with the application, through a listener the engine provides.
- tl-ide-jetty depends on jetty-ee10-websocket-jakarta-server (version managed with the other Jetty artifacts in the root POM; the transitive jetty-ee10-annotations is excluded, because its presence on the class path would switch on Jetty's annotation scanning and ServletContainerInitializer discovery in every WebAppContext). Bootstrap configures Jetty's JakartaWebSocketServletContainerInitializer on the web application context, so the servlet context carries the jakarta.websocket.server.ServerContainer attribute and Jetty's upgrade filter when the application starts.
- No classpath scanning: as before, the development start runs no annotation scanning and no ServletContainerInitializer discovery, so @ServerEndpoint classes are not found automatically there.
- com.top_logic.util.WebSocketEndpointListener (tl-core) is a ServletContextListener that registers the @ServerEndpoint classes named in the context parameter websocket.endpoints (fully qualified class names, separated by whitespace or commas) through the standard API ServerContainer.addEndpoint(Class). An application declares the listener and the parameter in its web.xml. This registration works on every Jakarta WebSocket container (Tomcat, Jetty with the websocket module, the development start). A missing container, a class that cannot be loaded or an endpoint the container rejects are logged as errors without stopping the application.
- The root POM manages jakarta.websocket-api and jakarta.websocket-client-api (2.1.1), so an application declares them with scope provided.
- The React demo application (tl-demo-react) registers an echo endpoint (/ws/echo) this way. test.com.top_logic.util.TestWebSocketEndpointListener verifies the listener in an embedded Jetty with a client round-trip.
- Developer article websocket-endpoints (in tl-core) describes the dependency, the endpoint class, the registration and a client example.
Migration
An application that works around the missing container (E-Home: jetty-ee10-websocket-jakarta-server as exec dependency and a web.xml listener running Jetty's initializer by name) removes that workaround and registers its endpoints through com.top_logic.util.WebSocketEndpointListener with the context parameter websocket.endpoints in its web.xml. Endpoints that relied on automatic discovery of @ServerEndpoint classes on Tomcat must be named in that parameter to be served by the development start as well.