Ether Framework
Unified API docs for Ether modules
Loading...
Searching...
No Matches
JettyWebSocketServerFactory.java
Go to the documentation of this file.
1package dev.rafex.ether.websocket.jetty12;
2
3/*-
4 * #%L
5 * ether-websocket-jetty12
6 * %%
7 * Copyright (C) 2025 - 2026 Raúl Eduardo González Argote
8 * %%
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 * #L%
27 */
28
29import java.nio.charset.StandardCharsets;
30import java.util.ArrayList;
31import java.util.LinkedHashMap;
32import java.util.List;
33import java.util.Map;
34import java.util.Objects;
35
36import org.eclipse.jetty.http.pathmap.PathSpec;
37import org.eclipse.jetty.server.Handler;
38import org.eclipse.jetty.server.Server;
39import org.eclipse.jetty.server.ServerConnector;
40import org.eclipse.jetty.server.handler.ContextHandler;
41import org.eclipse.jetty.util.MultiMap;
42import org.eclipse.jetty.util.UrlEncoded;
43import org.eclipse.jetty.util.thread.QueuedThreadPool;
44import org.eclipse.jetty.websocket.server.ServerUpgradeRequest;
45import org.eclipse.jetty.websocket.server.ServerUpgradeResponse;
46import org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler;
47
48import dev.rafex.ether.websocket.core.WebSocketPatterns;
49import dev.rafex.ether.websocket.core.WebSocketRoute;
50
51/**
52 * Factory that wires a Jetty {@link Server} with WebSocket routing.
53 *
54 * <p>This utility class provides static factory methods to create a
55 * {@link JettyWebSocketServerRunner} from a configuration and either a
56 * pre-built route registry or a list of {@link JettyWebSocketModule} instances.</p>
57 */
58public final class JettyWebSocketServerFactory {
59
60 private JettyWebSocketServerFactory() {
61 }
62
63 /**
64 * Creates a WebSocket server from the given configuration and route registry.
65 *
66 * <p>Configures a {@link QueuedThreadPool}, a {@link ServerConnector}, and a
67 * {@link WebSocketUpgradeHandler} that maps incoming upgrade requests to the
68 * registered endpoints.</p>
69 *
70 * @param config the server configuration (port, threads, timeout)
71 * @param routeRegistry the registry containing the WebSocket routes
72 * @return a runner that owns the configured {@link Server}
73 * @throws NullPointerException if {@code config} or {@code routeRegistry} is {@code null}
74 */
76 final JettyWebSocketRouteRegistry routeRegistry) {
77 Objects.requireNonNull(config, "config");
78 Objects.requireNonNull(routeRegistry, "routeRegistry");
79
80 final var pool = new QueuedThreadPool();
81 pool.setMaxThreads(config.maxThreads());
82 pool.setMinThreads(config.minThreads());
83 pool.setIdleTimeout(config.idleTimeoutMs());
84 pool.setName(config.threadPoolName());
85
86 final var server = new Server(pool);
87 final var connector = new ServerConnector(server);
88 connector.setPort(config.port());
89 server.addConnector(connector);
90
91 final var context = new ContextHandler("/");
92 context.setHandler(buildUpgradeHandler(server, routeRegistry.routes()));
93 server.setHandler(context);
94 return new JettyWebSocketServerRunner(server);
95 }
96
97 /**
98 * Creates a WebSocket server by invoking all given modules to populate the
99 * route registry, then delegates to {@link #create(JettyWebSocketServerConfig,
100 * JettyWebSocketRouteRegistry)}.
101 *
102 * @param config the server configuration
103 * @param modules the list of modules whose {@code registerRoutes} will be called
104 * @return a runner that owns the configured {@link Server}
105 */
107 final List<JettyWebSocketModule> modules) {
108 final var routeRegistry = new JettyWebSocketRouteRegistry();
109 final var context = new JettyWebSocketModuleContext(config);
110 for (final var module : modules == null ? List.<JettyWebSocketModule>of() : modules) {
111 module.registerRoutes(routeRegistry, context);
112 }
113 return create(config, routeRegistry);
114 }
115
116 /**
117 * Builds a {@link WebSocketUpgradeHandler} that maps each route's path spec
118 * to its endpoint adapter.
119 *
120 * @param server the Jetty server instance
121 * @param routes the WebSocket routes to register
122 * @return a configured upgrade handler
123 */
124 private static Handler buildUpgradeHandler(final Server server, final List<WebSocketRoute> routes) {
125 return WebSocketUpgradeHandler.from(server, container -> {
126 for (final var route : routes == null ? List.<WebSocketRoute>of() : new ArrayList<>(routes)) {
127 container.addMapping(PathSpec.from(route.pattern()),
128 (request, response, callback) -> createEndpoint(route, request, response));
129 }
130 });
131 }
132
133 /**
134 * Creates the endpoint adapter for a WebSocket upgrade request by extracting
135 * path params, headers, query params and negotiating subprotocols.
136 *
137 * @param route the matched route
138 * @param request the upgrade request
139 * @param response the upgrade response
140 * @return the endpoint adapter instance
141 */
142 private static Object createEndpoint(final WebSocketRoute route, final ServerUpgradeRequest request,
143 final ServerUpgradeResponse response) {
144 return JettyWebSocketUpgrades.createEndpoint(route, request, response);
145 }
146}
Mutable registry that collects WebSocket routes during module registration.
static JettyWebSocketServerRunner create(final JettyWebSocketServerConfig config, final JettyWebSocketRouteRegistry routeRegistry)
Creates a WebSocket server from the given configuration and route registry.
static JettyWebSocketServerRunner create(final JettyWebSocketServerConfig config, final List< JettyWebSocketModule > modules)
Creates a WebSocket server by invoking all given modules to populate the route registry,...
Thin wrapper around a Jetty Server that exposes lifecycle methods.
SPI contract for WebSocket modules that register endpoint routes.
Typed configuration entry points and composition APIs for Ether applications.
record JettyWebSocketServerConfig(int port, int minThreads, int maxThreads, int idleTimeoutMs, String threadPoolName)
Immutable configuration for the Jetty WebSocket server.
record JettyWebSocketModuleContext(JettyWebSocketServerConfig config)
Read-only context passed to JettyWebSocketModule#registerRoutes.