Deploying WebSocket Servers |
![]() |
This article shows how to host Net4j WS and WSS acceptors inside an application-managed Jetty 12.1 server. Net4j supplies named acceptors and routes accepted message connections into its connector lifecycle. The application remains responsible for Jetty listeners, TLS, handler composition, startup, and shutdown.
Table of Contents
| 1 | Features, Bundles, and Acceptor Configuration | ||
| 2 | Hosting WS over HTTP | ||
| 3 | Hosting WSS over HTTPS | ||
| 4 | Multiple Acceptors and Routing Isolation | ||
| 5 | Startup, Shutdown, and Failure Cleanup | ||
Install the org.eclipse.net4j.ws feature for the provider-neutral WS/WSS factories and the JDK client
provider. Install org.eclipse.net4j.ws.jetty.server when hosting on Jetty; its feature includes the core
feature and the Jetty adapter and requires Jetty 12.1 server and WebSocket modules. Keep the Jetty server lifecycle
with the application that embeds it.
WS and WSS acceptor entries in the CDO server configuration create named Net4j acceptor products. They do not create Jetty listeners or configure certificates. The following XML is a supported declaration of those named products:
A managed container can also create products programmatically. WSUtil.getAcceptor and
WSSUtil.getAcceptor obtain the corresponding factory type; activation registers the active acceptor name
in the Net4j acceptor manager. Use distinct names for separately exposed routes.
Create an initialized managed container, obtain and activate an acceptor, then construct an application-owned Jetty
Server, ServerConnector, and ContextHandler. Install
JettyWSUtil.createUpgradeHandler before starting Jetty. Its path spec must cover the service path and the
appended /@<acceptor-name> route; /net4j/* is a common mapping. The example binds to an
ephemeral loopback port and performs a bounded client connection to show the routing end to end.
The utility requires active, uniquely named acceptors and snapshots the supplied acceptor set for this handler. Each
upgrade must have a decoded path suffix and a Net4jAcceptor header naming the same acceptor. Requests to
an unknown or mismatched route receive 404; an insecure request sent to a WSS acceptor, or a secure request sent to a
WS acceptor, is rejected with 403.
WSS requires a WSS acceptor and a Jetty HTTPS listener. Configure the listener with an application-managed
SslContextFactory.Server, a key store containing the server private key and certificate chain, and a
SecureRequestCustomizer. Install the Net4j handler after activating the acceptor so the adapter can verify
the route. The example reads the key-store location and secret from deployment configuration; it does not disable
certificate or hostname validation.
The certificate must be valid for the host name clients use and must chain to a trust anchor they accept. Configure the
server identity with the net4j.tls.server.keyStore system property and
NET4J_TLS_SERVER_KEYSTORE_PASSWORD secret. Store passwords in a secret manager or protected environment
configuration, never in source control. Mutual TLS can be enabled
on the Jetty SSL context with setNeedClientAuth(true) and on the client's SSLContext with
client key managers when the deployment requires client certificates.
In normal application-controlled shutdown, the owner signals or interrupts this lifecycle thread so it leaves
server.join(). The finally block then stops Jetty before deactivating the Net4j acceptor and
its owned container.
Jetty's setStopAtShutdown(true) hook stops Jetty during JVM shutdown, but does not guarantee that the
waiting application thread resumes and executes this method's finally block. Do not rely on that hook for
Net4j cleanup. An embedded Jetty deployment must retain the server and coordinate Jetty and Net4j shutdown through the
embedding application's lifecycle.
One upgrade handler can expose several active acceptors. Pass only the names intended for that Jetty context; the Jetty
adapter snapshots this explicit allow-list and does not choose an arbitrary acceptor from the process-wide registry.
Use separate contexts, mappings, or servers for trust boundaries that must not share an acceptor set. Each request still
has to match the selected acceptor name in both its route path and Net4jAcceptor header.
The following excerpt shows the relevant public API call. The acceptors must already be active in the container and the handler must be installed before Jetty starts.
Start the Net4j container and activate each acceptor before creating the Jetty upgrade handler. Start Jetty only after its listeners and handler tree are complete. During shutdown, stop admission and drain application work, close any client connectors, stop the Jetty server, deactivate acceptors, and finally deactivate a container owned by this application. Do not deactivate a shared container as a way to close one connector.
If startup fails after Jetty begins initialization, stop the partially started server and release the acceptor and container in nested cleanup paths. A failed server start must not leave its acceptor registered as an active route.