Four small seams, each useful on its own, that together make a Flash app
testable without hand-rolled scaffolding.
- ServerHandle/ServerLifecycle/FlashApp gain port()/ports(). Listeners already
bind in the FlashApp constructor, so port(0) resolved to a real port that
nothing could read back; every integration test worked around this by
opening a ServerSocket(0), closing it and reusing the number, which races
anything else on the machine.
- FlashContext.override() replaces a binding instead of rejecting it. The
duplicate-is-an-error rule stays everywhere else; this is the single
deliberate exception, for swapping a service out in tests. A replacement is
logged at INFO so misuse in production is visible.
- FlashApplication + FlashApp.apply() name an application independently of the
port it runs on, so the same one can be booted twice. It takes FlashApp
rather than FlashRegistrar because ws() and mount() live there. Being a
functional interface, a lambda and a named class are the same thing.
- FlashContext.onClose() runs cleanup at stop(), children first and then in
reverse registration order. stop() previously closed sockets and the
executor and never touched the service graph, so a pooled DataSource was
only ever released by JVM exit — invisible with one app per process, a leak
per test class once a suite boots and stops many.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>