The compiler ignored the message a constraint declares and always wrote its
own, so a failed @Pattern answered the caller with a regex. It now uses the
annotation's message whenever one is set, and keeps the plain description for
jakarta's default, which is a resource bundle key and not something to put in
front of whoever sent the request.
This is what makes moving a check out of a service and onto the type it
belongs to cost nothing: the wording moves with it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
flash-ext-jackson and flash-ext-validation become three modules:
- flash-ext-jackson-core: the Codec (one mapper, body/write/writeView),
JacksonHandler, the outbound marshalling, and the constraint engine that
used to be flash-ext-validation
- flash-ext-jackson-json: Json, JsonExtension, JsonHandler
- flash-ext-jackson-xml: Xml, XmlExtension, XmlHandler
Every Jackson format is the same databind model behind a different factory, so
the annotations, the constraints and the published schema are the same for all
of them: only the mapper and the content type differ, and that is all a format
module says. A route picks its format by the handler it extends — there is no
negotiation and nothing to configure.
A typed body is now always verified against its own type's jakarta
constraints, whatever the format: malformed is a 400, a broken constraint is a
422, and neither reaches the handler. The validator was already allocation-free
and stays so; validating is no longer something an application remembers to do.
bodyFrom is gone. body has the streaming semantics, because the request's
stream is reused per connection while bytes() allocates the whole body: one
name, the path that does not allocate. JacksonExtension is JsonExtension, and
autoJson() is auto().
The root POM now manages every module of this build, so anything composing
Flash imports it once and never names a version again.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>