GuicedEE Web
Reactive HTTP/HTTPS server bootstrap for GuicedEE applications using Vert.x 5.
Core Concept
Provides the Router, HttpServer, and BodyHandler plumbing that higher-level modules (rest, websockets, etc.) build on top of. Configuration is environment-driven; extension is SPI-driven. The server starts automatically via IGuicePostStartup.
Required Flow
- Add
com.guicedee:webdependency. - Bootstrap GuicedEE — the web server starts automatically:
IGuiceContext.registerModuleForScanning.add("my.app"); IGuiceContext.instance().inject(); // Startup is asynchronous; await getLoadingFinished() before asserting readiness. - Add routes by implementing
VertxRouterConfigurator:public class MyRoutes implements VertxRouterConfigurator<MyRoutes> { @Override public Router builder(Router router) { router.get("/health").handler(ctx -> ctx.response().end("OK")); return router; } @Override public Integer sortOrder() { return 500; } } - Register via JPMS:
module my.app { requires com.guicedee.vertx.web; provides com.guicedee.vertx.web.spi.VertxRouterConfigurator with my.app.MyRoutes; }
SPI Extension Points
All SPIs are discovered via ServiceLoader. Register with JPMS provides...with and META-INF/services for both launch modes.
| SPI | When it runs | Purpose |
|---|---|---|
| VertxHttpServerOptionsConfigurator | Before servers created | Customize HttpServerOptions (ports, TLS, compression) |
| VertxHttpServerConfigurator | After server creation | Configure HttpServer instance (WebSocket upgrade, connection hooks) |
| VertxRouterConfigurator | After body handler | Add routes, middleware, handlers to the Router |
Configuration
All configuration is driven by system properties or environment variables:
| Variable | Default | Purpose |
|---|---|---|
| HTTP_ENABLED | true | Enable HTTP server |
| HTTP_PORT | 8080 | HTTP listen port |
| HTTPS_ENABLED | false | Enable HTTPS server |
| HTTPS_PORT | 443 | HTTPS listen port |
| HTTPS_KEYSTORE | — | Path to JKS or PKCS#12 keystore |
| HTTPS_KEYSTORE_PASSWORD | — | Keystore password |
| VERTX_MAX_BODY_SIZE | 524288000 (500 MB) | Maximum request body size |
TLS/HTTPS
Keystore format is auto-detected by file extension:
.jks→ JKS.pfx,.p12,.p8→ PKCS#12
Startup Flow
IGuiceContext.instance().inject()
└─ IGuicePostStartup hooks
└─ VertxWebServerPostStartup (sortOrder = MIN_VALUE + 500)
├─ Build HttpServerOptions (compression, keepalive, header limits)
├─ Apply VertxHttpServerOptionsConfigurator SPIs
├─ Create HTTP / HTTPS servers
├─ Apply VertxHttpServerConfigurator SPIs
├─ Create Router + BodyHandler
├─ Apply VertxRouterConfigurator SPIs (sorted)
├─ Mount per-verticle sub-routers
├─ Configure Jackson ObjectMapper via IJsonRepresentation
└─ server.listen()
Isolated management listeners
Use ManagedHttpListenerProvider.listeners() for additional lifecycle-owned listeners, such as an internal health or management port. Return a list of ManagedHttpListener(name, options, vertx -> router) records and dual-register the provider in module-info.java and META-INF/services/com.guicedee.vertx.web.spi.ManagedHttpListenerProvider.
ManagedHttpListenerGroup deliberately creates a dedicated small Vert.x runtime to isolate its socket registry from the public server. It validates unique names and fixed ports, disables address/port reuse, rejects WebSocket upgrades, rolls back partially bound listeners on failure, and closes its runtime on shutdown. Build each router using the supplied Vert.x instance. Global router/server configurators are deliberately not applied; configure the listener's own routes and access policy explicitly.
ManagedHttpListenersLifecycle starts at MIN_VALUE + 800 and awaits binding. Executable mains can use GuiceApplication.run(...) from inject to await overall startup; embedded callers must observe getLoadingFinished() themselves.
WebSocket upgrade and slow-peer shutdown
Preserve WebSocketBackpressure.capture(request) at the server's request-handler handoff to the configured router. Capture happens before Vert.x Web applies forwarded-header wrappers; routing a plain request can lose the underlying transport needed for bounded shutdown.
WebSocketBackpressure.close supports raw ServerWebSocket and captured route upgrades. With Vert.x 5.2, the normal close timeout starts after the close frame flushes. A peer that stops reading can block that flush, so the helper separately schedules Netty transport closure after two seconds. Transport closure must trigger idempotent connection and subscription cleanup.
The integration uses exported Vert.x io.vertx.core.internal.http interfaces and direct io.netty.transport/io.netty.common module dependencies. It requires neither reflective wrapper inspection nor JVM --add-exports flags. Captured transport lookup uses header-object identity, avoiding collisions from reused WebSocket keys, and is removed when the channel closes. Recheck these version-specific interfaces when upgrading Vert.x.
Exercise both the direct server handler and public router upgrade with a real peer that stops reading. A successful close-handshake unit test does not prove this deadline.
Non-Negotiable Constraints
- Public HTTP uses
VertxWebServerPostStartup; additional isolated listeners useManagedHttpListenerProvider. Isolated tests may explicitly create and close their own servers. - Module must
requires com.guicedee.vertx.web;. - SPI implementations must be dual-registered (
module-info.java+META-INF/services/). VertxRouterConfiguratorimplementations control ordering viasortOrder().