This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch docs
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/docs by this push:
     new c80e9b9fe9 docs: add 10.07b mixin sub-RestContext topic, cross-link 
from 10.07a, and 9.5.0 release notes
c80e9b9fe9 is described below

commit c80e9b9fe9298d95821c9493d4850d45ab5b197f
Author: James Bognar <[email protected]>
AuthorDate: Sun May 24 13:24:16 2026 -0400

    docs: add 10.07b mixin sub-RestContext topic, cross-link from 10.07a, and 
9.5.0 release notes
    
    Co-authored-by: Cursor <[email protected]>
---
 pages/release-notes/9.5.0.md                      |  39 +++
 pages/topics/10.07a.RestServerComposition.md      |  50 ++-
 pages/topics/10.07b.RestServerMixinSubContexts.md | 404 ++++++++++++++++++++++
 3 files changed, 482 insertions(+), 11 deletions(-)

diff --git a/pages/release-notes/9.5.0.md b/pages/release-notes/9.5.0.md
index 1776792120..df8ea7cfc1 100644
--- a/pages/release-notes/9.5.0.md
+++ b/pages/release-notes/9.5.0.md
@@ -2056,6 +2056,45 @@ Users can now choose either:
 - **Recommended**: `@Rest(mixins=BasicHealthResource.class)` on the root 
resource (single servlet).
 - **Fallback**: standalone `HealthProbeConfiguration` auto-mount (separate 
servlet, explicit paths).
 
+#### Mixin Sub-Context Inheritance
+
+`@Rest(mixins=…)` mixin classes are now each elevated into a dedicated 
`RestContext` sub-context
+rather than being treated as bare method libraries grafted into the host's 
context.  Each mixin
+gets its own bean store, hook chain, and contribution lists; the host's 
`RestContext` becomes the
+mixin's `parentContext`, and the mixin inherits configuration via the same 
parent-walk used for
+the rest of the `@Rest` annotation chain — with `@Rest(noInherit={…})` 
available as the per-property
+opt-out (semantics aligned with the rest of the framework).
+
+- **Inheritance walk.** `RestContext.getRestAnnotationsForProperty(name)` 
prepends the host's
+  `@Rest` chain onto the mixin's own so the reduce-last / append-all walks all 
the contribution
+  lists transparently. Verified by a per-list regression matrix covering 
serializers, parsers,
+  encoders, converters, response processors, REST op args, guards (default 
inherit + `noInherit`
+  opt-out), callLogger, debugEnablement, debugDefault, partSerializer, and 
partParser. None of
+  these lists needed bespoke wiring — the walk drives them all.
+- **Hook dual-firing.** `@RestStartCall` / `@RestEndCall` / `@RestPreCall` / 
`@RestPostCall` /
+  `@RestDestroy` now fire host-first-then-mixin for mixin-endpoint requests 
and host-only for
+  host-endpoint requests. Hook execution order is the standard host→mixin 
chain regardless of
+  which mixin class declared the operation that's being dispatched, so 
per-request
+  observability (logging, metrics, request-id injection) stays uniform across 
host and mixin
+  endpoints.
+- **Messages chaining.** `Messages` doesn't ride the `@Rest` walk (it has its 
own
+  parent-resource-bundle chain semantics), so the new `Messages.chain(child, 
parent)` static
+  factory composes two pre-built bundles into a single parent-chained bundle 
without mutating
+  either input. The `messages` memoizer uses it to inherit the host's bundle 
on mixin sub-contexts
+  (mixin-keys-first, host-keys-as-fallback). `@Rest(noInherit={"messages"})` 
cuts the chain off
+  for callers that want isolation.
+- **`DefaultConfig` synthesis is host-only.** The framework's synthesized 
`DefaultConfig` entry on
+  a bare `@Rest` resource is now skipped for mixin sub-contexts, because the 
host's chain already
+  supplies it. Without this, a mixin without an explicit per-property 
declaration would silently
+  pick up the framework's `DefaultConfig` default *after* the host's explicit 
value in the
+  combined parent-walk — i.e. mixins would shadow host overrides for any 
property they didn't
+  declare themselves. The skip is invisible to user code; it just makes "no 
declaration on the
+  mixin" actually inherit from the host.
+- **New canonical `RestContext.Args` constructor.** The legacy 8-arg 
back-compat constructor was
+  removed; the canonical 9-arg form now takes an explicit `mixinContext` flag 
(host=`false`,
+  mixin sub-context=`true`). No external callers exist; all internal call 
sites in
+  `juneau-rest-server`, `juneau-rest-mock`, and `juneau-commons` were updated.
+
 #### Runtime-Overridable `@Rest(paths=...)` Resolution Chain (TODO-73)
 
 The `paths` array on `@Rest` is now a *default* in a three-rung 
**runtime-override resolution
diff --git a/pages/topics/10.07a.RestServerComposition.md 
b/pages/topics/10.07a.RestServerComposition.md
index c049be2e72..d409774938 100644
--- a/pages/topics/10.07a.RestServerComposition.md
+++ b/pages/topics/10.07a.RestServerComposition.md
@@ -6,10 +6,13 @@ slug: RestServerCompositionMixinsAndPaths
 Juneau REST servers ship two complementary composition / mounting primitives 
on the class-level
 [`@Rest`](/site/apidocs/org/apache/juneau/rest/annotation/Rest.html) 
annotation:
 
-- **`@Rest(mixins=Class<?>[])`** — *composition without inheritance.* Graft 
every
+- **`@Rest(mixins=Class<?>[])`** — *inline composition with per-mixin 
sub-contexts.* Graft every
   `@RestOp`-group method (`@RestGet`, `@RestPost`, `@RestPut`, `@RestPatch`, 
`@RestDelete`,
   `@RestOptions`, `@RestOp`) from a listed class into the importing resource's 
operation tree.
-  Local methods on the importing resource win on path/method collisions.
+  Local methods on the importing resource win on path/method collisions. 
Starting in 9.5.0 each
+  mixin owns its own `RestContext` parent-linked to the host's, so the mixin 
can declare
+  serializers / guards / hooks / call-logger / messages that apply only to its 
endpoints — see
+  [Mixin Sub-Contexts](/docs/topics/RestServerMixinSubContexts) for the 
inheritance model.
 - **`@Rest(paths=String[])`** — *multi-mount for top-level servlets.* Mount a 
single
   `RestServlet` instance under multiple exact URL patterns. Primarily intended 
for back-compat-
   friendly add-on endpoints (probes, well-known paths) that need to live at 
fixed URLs without
@@ -329,14 +332,32 @@ mixin's own.
 
 A few intentional limitations to be aware of.
 
-### Mixin scope is `@RestOp` methods only
+### Mixin scope and per-mixin RestContext (since 9.5.0)
 
-The mixin walk grafts `@RestOp`-group **methods** from the mixin class. It 
does **not** inherit:
+The mixin walk grafts `@RestOp`-group **methods** from the mixin class. 9.5.0 
promotes each
+mixin class to its own `RestContext` parent-linked to the host's, so the 
following mixin-class
+declarations now apply specifically to that mixin's endpoints (host endpoints 
are unaffected):
+
+- `@Rest(serializers=..., parsers=..., encoders=...)` — append to the host's 
chain for mixin
+  endpoints only.
+- `@Rest(converters=..., responseProcessors=..., restOpArgs=..., guards=...)` 
— same: appended
+  to the host's chain for mixin endpoints. **Guards inherit by default** — see 
the
+  [Mixin 
Sub-Contexts](/docs/topics/RestServerMixinSubContexts#guard-inheritance-security-note)
+  topic for the security rationale.
+- `@Rest(callLogger=..., debugEnablement=..., debugDefault=...)` — per-mixin 
override that wins
+  over the host's value for mixin endpoints.
+- `@Rest(messages=..., partSerializer=..., partParser=...)` — chained / 
overridden per mixin.
+- `@RestStartCall` / `@RestEndCall` / `@RestPreCall` / `@RestPostCall` / 
`@RestDestroy` —
+  dual-fire, host-then-mixin for mixin-endpoint requests; host-only for 
host-endpoint requests.
+
+`@Rest(noInherit={...})` on a mixin class cuts off inheritance per property 
using the same token
+set as the host's `@Rest(noInherit={...})` machinery. The full inheritance 
model — including the
+flat-inheritance rule for transitive mixins, hook ordering, and the 
`Messages.chain(...)` seam
+that makes message-bundle inheritance work — is documented in the
+[Mixin Sub-Contexts](/docs/topics/RestServerMixinSubContexts) topic.
+
+The mixin walk still does **not** inherit:
 
-- The mixin's class-level `@Rest(serializers=..., parsers=..., encoders=..., 
guards=..., converters=..., ...)`
-  configuration. The importing resource's `@Rest` config applies to mixin 
operations — including
-  serializers, parsers, content negotiation, response processors, error 
handling, guards,
-  converters, and so on.
 - The mixin's class-level `@HtmlDocConfig` / `@JsonConfig` / other config 
annotations.
 - The mixin's `@Rest(children=...)` child resources. Mixins don't compose 
child trees.
 - The mixin's `@Rest(path=...)` or `@Rest(paths=...)` — the importing 
resource's path / paths
@@ -350,8 +371,11 @@ The mixin walk grafts `@RestOp`-group **methods** from the 
mixin class. It does
   per-instance state on the mixin is honored; it just isn't visible to the 
importing resource's
   local methods or to other mixins.
 
-If you need the mixin's full config, the right tool is `@Rest(children=...)` 
(or an explicit
-servlet mount via `@Rest(paths=...)`), not `mixins`.
+If you need the addon to mount under its own URL subtree with full resolution 
isolation from the
+host, the right tool is `@Rest(children=...)`, not `mixins`. Children are 
deliberately isolated
+from the parent's resolution chain — see
+[Mixin Sub-Contexts § Mixin-vs-child 
divergence](/docs/topics/RestServerMixinSubContexts#mixin-vs-child-divergence)
+for the rationale.
 
 ### `paths` is exact-match only
 
@@ -515,12 +539,16 @@ own Swagger / OpenAPI scope.
 
 ## See also
 
+- [Mixin Sub-Contexts](/docs/topics/RestServerMixinSubContexts) — the 
per-mixin `RestContext`
+  inheritance model, `noInherit` opt-out, hook dual-firing, flat-inheritance 
rule, and
+  mixin-vs-child divergence.
 - [Health / Readiness / Liveness Probes](/docs/topics/HealthProbes) — the 
canonical consumer of
   both primitives, with full Option A (mixin) and Option B (standalone 
auto-mount) examples.
 - [@Rest-Annotated Class Basics](/docs/topics/RestAnnotatedClassBasics) — the 
broader `@Rest`
   attribute reference.
 - [Child Resources](/docs/topics/ChildResources) — the alternative composition 
primitive when
-  you want a separate `RestContext` and bean store per addon.
+  you want a separate `RestContext` and bean store per addon, isolated from 
the host's
+  resolution chain.
 - [Path Patterns](/docs/topics/PathPatterns) — operation-level path matching 
(the layer mixin
   routes are grafted *into*).
 - [REST Server Overview](/docs/topics/RestServerOverview) — where these 
primitives sit in the
diff --git a/pages/topics/10.07b.RestServerMixinSubContexts.md 
b/pages/topics/10.07b.RestServerMixinSubContexts.md
new file mode 100644
index 0000000000..e6f5bce39e
--- /dev/null
+++ b/pages/topics/10.07b.RestServerMixinSubContexts.md
@@ -0,0 +1,404 @@
+---
+title: "REST Server — Mixin Sub-Contexts"
+slug: RestServerMixinSubContexts
+---
+
+`@Rest(mixins=...)` got a structural upgrade in 9.5.0. The user-facing API is 
unchanged
+(`@Rest(mixins=Foo.class, Bar.class)` still grafts the listed classes' 
`@RestOp` methods into the
+importing resource's operation tree), but the *implementation* now elevates 
each mixin from a
+"method library absorbed into the host's `RestContext`" to an "embedded 
sub-resource with its own
+`RestContext` parent-linked to the host's."
+
+Pre-9.5.0, mixin endpoints resolved serializers, parsers, guards, hooks, etc. 
through the host's
+single `RestContext`. There was no place for mixin-scoped configuration — the 
mixin's
+class-level `@Rest(serializers=..., guards=..., ...)` declarations were 
silently ignored. Starting
+in 9.5.0, each mixin owns its own `RestContext` whose `parentContext` points 
at the host's, and
+inheritance walks parent → mixin per property. The mixin can append to or 
replace each
+contribution list, and the framework's `@Rest(noInherit={...})` opt-out blocks 
inheritance per
+property.
+
+> This is the *opposite* design choice from `@Rest(children=...)`. Children are
+> deliberately isolated from the parent's resolution chain. Mixins inherit. 
The divergence is
+> intentional — see [Mixin-vs-child divergence](#mixin-vs-child-divergence) 
below.
+
+This topic covers the inheritance model, the `noInherit` opt-out, hook 
dual-firing, the
+flat-inheritance rule for transitive mixins, per-mixin overrides, the new
+`Messages.chain(...)` seam, and the guard inheritance security default.
+
+## What changed and why
+
+Pre-9.5.0 mixins were "method libraries absorbed into the host's 
`RestContext`." Mixin classes
+that declared `@Rest(serializers=..., guards=..., hooks=..., callLogger=..., 
...)` got those
+contributions silently ignored — every mixin endpoint resolved through the 
host's resolver chain
+with no place for mixin-scoped overrides. 9.5.0 promotes each mixin to "an 
embedded sub-resource,
+each with their own `RestContext` parent-linked to the host." Mixin endpoints 
now resolve through
+the mixin's `RestContext`, which inherits the host's contributions (parent 
walk) and optionally
+appends or replaces them.
+
+The rationale, quoted from the resolved-decisions log on the work item that 
drove this redesign:
+
+> "Children are completely independent from parents — they're 
externally-mounted at their own URL
+> namespace, often have their own deployment lifecycle (factored into separate 
jars, etc.), and
+> conflating their resolution with the host's would surprise authors who treat 
children as
+> standalone REST endpoints that happen to be discovered through a parent. 
Mixins are different:
+> they're explicitly *inline*, share the host's URL namespace, and exist to 
compose with the
+> host. Inheritance is the right model for mixins; isolation is the right 
model for children."
+
+That divergence is the heart of the 9.5.0 mixin model. The rest of this page 
is the user-facing
+reference for what inherits, what doesn't, and how to opt out.
+
+## The host-to-mixin inheritance model
+
+The canonical motivating use case is YAML serialization for OpenAPI documents. 
A REST app's
+`/items` endpoint should not advertise `application/yaml` in its 
`Content-Type` negotiation, but
+its `/openapi.yaml` endpoint must serve YAML. With mixin sub-context 
inheritance, the YAML
+serializer can live on the mixin alone:
+
+```java
+// Mixin: declares a YamlSerializer that applies only to mixin endpoints.
+@Rest(
+    paths = {"/openapi", "/openapi.json", "/openapi.yaml"},
+    serializers = {YamlSerializer.class}
+)
+public class OpenApiResource {  // (illustrative — full BasicOpenApiResource 
lands via TODO-74)
+    @RestGet(path="/openapi.yaml")
+    public OpenApi yaml(RestRequest req) { return req.getOpenApi(); }
+    // ... /openapi and /openapi.json handlers ...
+}
+
+// Host: a vanilla RestServlet, no YAML declared.
+@Rest(mixins = OpenApiResource.class)
+public class ApiResource extends RestServlet {
+    @RestGet("/items")
+    public List<Item> items() { ... }
+}
+```
+
+After construction, the resolved serializer sets look like this:
+
+| Context                          | Resolved serializers                      
   |
+|----------------------------------|----------------------------------------------|
+| `ApiResource` `RestContext`      | host defaults (JSON, XML, HTML, …)        
   |
+| `OpenApiResource` mixin sub-ctx  | host defaults (JSON, XML, HTML, …) + 
`YamlSerializer` |
+
+Concrete request dispatch:
+
+| Request                                              | Resolves through      
  | Result                                |
+|------------------------------------------------------|-------------------------|---------------------------------------|
+| `GET /items`                                         | host context          
  | JSON/XML/HTML negotiation as normal   |
+| `GET /items` `Accept: application/yaml`              | host context          
  | 406 Not Acceptable (host has no YAML) |
+| `GET /openapi.yaml`                                  | mixin sub-context     
  | 200, YAML body                        |
+| `GET /openapi` `Accept: application/yaml`            | mixin sub-context     
  | 200, YAML body                        |
+
+The mixin's `@Rest(serializers={YamlSerializer.class})` declaration *appends* 
to the inherited
+host set, so the mixin sub-context sees the full host serializer chain plus 
YAML. The host's
+chain is unaffected — `/items` doesn't grow a YAML serializer just because 
YAML appears on a
+mixin.
+
+## The contribution lists that inherit
+
+The Phase 2 inheritance walk extends 
`RestContext.getRestAnnotationsForProperty(name)` to prepend
+the parent's `@Rest` chain onto the mixin's own. That single change cascades 
to every
+contribution list because the `*Builder` memoizers and configuration accessors 
on `RestContext`
+all read through `getRestAnnotationsForProperty(...)`. The framework verifies 
inheritance for the
+following thirteen contribution lists in 
`juneau-utest/src/test/java/org/apache/juneau/rest/mixin/`
+(one regression test class per list):
+
+| `@Rest` property         | Inheritance behavior                              
                              | `noInherit` token       |
+|--------------------------|---------------------------------------------------------------------------------|-------------------------|
+| `serializers`            | Host first, then mixin appended. Mixin endpoint 
sees union.                     | `"serializers"`         |
+| `parsers`                | Host first, then mixin appended. Mixin endpoint 
sees union.                     | `"parsers"`             |
+| `encoders`               | Host first, then mixin appended. Mixin endpoint 
sees union.                     | `"encoders"`            |
+| `partSerializer`         | Mixin overrides host if declared; else inherits 
host's resolved value.          | `"partSerializer"`      |
+| `partParser`             | Mixin overrides host if declared; else inherits 
host's resolved value.          | `"partParser"`          |
+| `converters`             | Host first, then mixin appended. Mixin endpoint 
runs both chains.               | `"converters"`          |
+| `responseProcessors`     | Host first, then mixin appended. Mixin endpoint 
runs both chains.               | `"responseProcessors"`  |
+| `restOpArgs`             | Host first, then mixin appended. Mixin can add 
custom arg resolvers.            | `"restOpArgs"`          |
+| `guards`                 | Host first, then mixin appended. 
**Inherit-by-default for security.**           | `"guards"`              |
+| `callLogger`             | Mixin overrides host if declared; else inherits 
host's resolved value.          | `"callLogger"`          |
+| `debugEnablement`        | Mixin overrides host if declared; else inherits 
host's resolved value.          | `"debugEnablement"`     |
+| `debugDefault`           | Mixin overrides host if declared; else inherits 
host's resolved value.          | `"debugDefault"`        |
+| `messages`               | Mixin's bundle chained as child of host's via 
`Messages.chain(...)`.            | `"messages"`            |
+
+"Host first, then mixin appended" means the mixin endpoint's resolver chain is 
the concatenation
+of the host's and the mixin's, in that order. For *list-shaped* properties 
(serializers,
+parsers, encoders, converters, response processors, REST op args, guards), the 
chain produced is
+the union — useful for accumulating contributions. For *replace-shaped* 
properties (partSerializer,
+partParser, callLogger, debugEnablement, debugDefault), the mixin's value 
(when present) wins
+over the host's because the walk uses last-write-wins on the property's 
annotation chain.
+
+Tokens in the `noInherit` column are the exact case-insensitive strings to put 
in
+`@Rest(noInherit={...})` on the mixin to cut off inheritance for that property 
— semantics
+identical to the framework's existing `@RestOp(noInherit={...})` and 
`@Rest(noInherit={...})`
+machinery on the host's own class chain.
+
+## Hook dual-firing
+
+Lifecycle hooks (`@RestStartCall`, `@RestEndCall`, `@RestPreCall`, 
`@RestPostCall`, and
+`@RestDestroy`) participate in the mixin model with a strict ordering contract:
+
+1. **Host-endpoint request.** Only host hooks fire. The mixin's hook chains 
are not visited.
+2. **Mixin-endpoint request.** Host hooks fire first; then the mixin's hooks 
fire. Hooks declared
+   on the host's class always run before hooks declared on the mixin's class, 
even for requests
+   that land on mixin endpoints. The dispatcher walks the parent context's 
invoker list before
+   the mixin's.
+3. **Application shutdown.** `@RestDestroy` fires on every context — host 
first, then each
+   mixin's destroy chain in mixin-discovery order.
+
+The "host first" rule is the natural consequence of the parent-walk model: 
from a mixin
+sub-context's point of view, the host context is its parent, and parent hooks 
run before the
+local class's hooks (consistent with how the framework has always handled 
`@RestStartCall` on a
+parent class vs. a subclass).
+
+Putting it together, a `GET /openapi.yaml` request against the example above 
traces this
+sequence:
+
+1. `ApiResource`'s `@RestStartCall` methods (host's start-call invoker list).
+2. `OpenApiResource`'s `@RestStartCall` methods (mixin's start-call invoker 
list).
+3. Host's `@RestPreCall` methods.
+4. Mixin's `@RestPreCall` methods.
+5. The matched `@RestGet` operation method on `OpenApiResource`.
+6. Mixin's `@RestPostCall` methods.
+7. Host's `@RestPostCall` methods.
+8. Mixin's `@RestEndCall` methods.
+9. Host's `@RestEndCall` methods.
+
+(`@RestPreCall` is host-then-mixin to match the start-call ordering. 
`@RestEndCall` and
+`@RestPostCall` reverse the order — mixin-then-host — so unwinds match 
initialization, like
+nested try/finally blocks.)
+
+## The `noInherit` opt-out per property
+
+Every contribution list inherits by default. `@Rest(noInherit={...})` on the 
mixin's class is the
+opt-out — the token set is the same as the property names in the table above, 
and the framework's
+existing `noInherit` machinery handles them uniformly. Three worked examples:
+
+**`noInherit={"serializers"}`** — the mixin owns its serializer chain, no host 
inheritance.
+
+```java
+// Octet-stream-only "raw blob" mixin: only mixin's serializers, no host 
JSON/XML/HTML.
+@Rest(
+    noInherit = {"serializers", "parsers"},
+    serializers = {OctetStreamSerializer.class},
+    parsers = {OctetStreamParser.class}
+)
+public class RawBlobResource {
+    @RestPut(path="/blob") public void put(byte[] body) { ... }
+    @RestGet(path="/blob") public byte[] get() { ... }
+}
+```
+
+Mixin endpoints emit and accept `application/octet-stream` exclusively. Host 
endpoints retain
+whatever serializer chain they had.
+
+**`noInherit={"guards"}`** — the mixin opts out of host guard protection.
+
+```java
+// Health probe mixin: explicitly unguarded even if host has BearerTokenGuard.
+@Rest(
+    paths = {"/healthz", "/readyz", "/livez"},
+    noInherit = {"guards"}
+)
+public class HealthResource extends BasicHealthResource { ... }
+```
+
+Without `noInherit={"guards"}`, the host's `BearerTokenGuard` would also 
protect `/healthz` — the
+inherit-by-default behavior is the security-conservative choice (see [Guard 
inheritance security
+note](#guard-inheritance-security-note) below).
+
+**`noInherit={"messages"}`** — the mixin owns its message bundle, no host 
fallthrough.
+
+```java
+@Rest(
+    messages = "MixinMessages",
+    noInherit = {"messages"}
+)
+public class MixinWithIsolatedMessages { ... }
+```
+
+Mixin's `$L{key}` lookups resolve from `MixinMessages.properties` only — they 
do not fall through
+to the host's bundle. Without `noInherit`, the mixin's bundle is chained as a 
child of the host's
+via `Messages.chain(...)` (see [Messages.chain(...) as the public 
seam](#messageschain-as-the-public-seam)
+below) so any key absent from the mixin's bundle resolves through the host's.
+
+## Flat inheritance for transitive mixins
+
+A mixin can declare `@Rest(mixins=...)` of its own. The framework discovers 
the transitive
+closure and registers every reachable mixin against the host. **All discovered 
mixins
+parent-link directly to the host** — never to a transitive predecessor. If `A` 
mixes in `B`,
+both `A` and `B` end up with `parentContext = host`. They do *not* form an `A 
→ B` inheritance
+chain.
+
+```java
+@Rest(serializers=YamlSerializer.class)
+public class B {
+    @RestGet(path="/b") public String b() { return "b"; }
+}
+
+@Rest(mixins=B.class, serializers=ProtobufSerializer.class)
+public class A {
+    @RestGet(path="/a") public String a() { return "a"; }
+}
+
+@Rest(mixins=A.class, serializers=XmlSerializer.class)
+public class HostResource extends BasicRestServlet { ... }
+```
+
+Resolved serializer sets:
+
+| Context                      | Parent      | Resolved serializer chain       
                     |
+|------------------------------|-------------|------------------------------------------------------|
+| `HostResource` (host)        | (none)      | `XmlSerializer`                 
                     |
+| `A` mixin sub-ctx            | host        | `XmlSerializer`, 
`ProtobufSerializer`                |
+| `B` mixin sub-ctx            | host        | `XmlSerializer`, 
`YamlSerializer`                    |
+
+Notice the flat structure: `B`'s sub-context inherits from the host 
(`XmlSerializer`), not from
+`A` (no `ProtobufSerializer`). The rationale, again from the 
resolved-decisions log:
+
+> "Predictable: a mixin always inherits from 'the host,' period. 
Order-independent: the discovery
+> order of A and B doesn't matter for B's resolution. Composable: a mixin can 
be added or removed
+> without rearranging inheritance chains."
+
+If a use case for cross-mixin inheritance ever emerges, it'll be modeled as an 
explicit
+mechanism (some new annotation member) rather than by reshaping the discovery 
walk. The
+flat-inheritance rule is load-bearing for the v1 model.
+
+## Per-mixin overrides for host settings
+
+Because each mixin owns its own `RestContext`, it can declare any host setting 
— including
+typically host-wide concerns like `callLogger`, `debugEnablement`, and 
`guards` — and the
+override applies *only* to that mixin's endpoints. The host's resolution chain 
is unaffected.
+
+```java
+// Host: vanilla call-logging, no admin protection.
+@Rest(
+    mixins = AdminResource.class,
+    callLogger = HostCallLogger.class
+)
+public class ApiResource extends BasicRestServlet {
+    @RestGet("/items") public List<Item> items() { ... }
+}
+
+// Mixin: structured JSON logging, BearerToken-only.
+@Rest(
+    paths = {"/admin/threads", "/admin/heap"},
+    callLogger = StructuredJsonLogger.class,
+    debugEnablement = AdminDebugEnablement.class,
+    guards = AdminBearerGuard.class
+)
+public class AdminResource {
+    @RestGet(path="/admin/threads") public String threads() { ... }
+    @RestGet(path="/admin/heap") public String heap() { ... }
+}
+```
+
+Concrete observability:
+
+| Request                | callLogger              | debugEnablement          
| Guard chain                                           |
+|------------------------|-------------------------|--------------------------|-------------------------------------------------------|
+| `GET /items`           | `HostCallLogger`        | host's default           
| host's (none in this example)                         |
+| `GET /admin/threads`   | `StructuredJsonLogger`  | `AdminDebugEnablement`   
| host's (none) + `AdminBearerGuard` (inherited+appended) |
+
+The admin mixin's override is scoped to admin endpoints — it doesn't bleed 
back into the host's
+`/items` logging or debugging. If the mixin wanted *only* its own logger (no 
inheritance), it
+would add `noInherit={"callLogger"}` (though for callLogger and the other 
replace-shaped
+properties, "declare an explicit value on the mixin" already wins over the 
parent walk — `noInherit`
+is mostly meaningful for the list-shaped properties where inheritance produces 
a union).
+
+## `Messages.chain(...)` as the public seam
+
+`@Rest(messages=...)` doesn't ride the standard 
`getRestAnnotationsForProperty(...)` walk —
+`Messages` has its own ResourceBundle-based parent-chain mechanism that 
pre-dates this work item.
+To keep mixin-Messages inheritance working without a special-case in 
`Messages.Builder`, 9.5.0
+introduces a new static factory on `Messages`:
+
+```java
+public static Messages chain(Messages child, Messages parent)
+```
+
+`Messages.chain(child, parent)` returns a new parent-chained bundle whose 
lookups read `child`
+first and fall through to `parent` for any key not found in `child`'s chain. 
Neither input is
+mutated: the factory deep-copies `child`'s existing `parent2` chain, splicing 
`parent` in at the
+deepest position. The new chain shares the inner `ResourceBundle` instances 
with the inputs
+(read-only, so safe).
+
+The mixin's `RestContext.messages` memoizer uses it like this:
+
+```java
+var local = build the mixin's own Messages bundle from @Rest(messages=...);
+return Messages.chain(local, hostContext.getMessages());
+```
+
+That single line makes the mixin's bundle inherit from the host's. 
`@Rest(noInherit={"messages"})`
+on the mixin skips the wrap so the mixin sees its bundle in isolation.
+
+The factory is a general-purpose composition primitive — user code that needs 
to thread one
+pre-built `Messages` bundle through another (a CMS-style overlay of per-tenant 
strings on top of
+app-wide strings, for example) can call `Messages.chain(...)` directly without 
going through the
+mixin machinery.
+
+## Mixin-vs-child divergence
+
+**Mixins inherit from the host. Children do not.** This is the central design 
decision of the
+9.5.0 mixin model, and it's the one to internalize before reaching for either 
primitive. From
+the resolved-decisions log:
+
+> "Child resources tend to be completely independent from parents — they're 
externally-mounted at
+> their own URL namespace, often have their own deployment lifecycle (factored 
into separate
+> jars, etc.), and conflating their resolution with the host's would surprise 
authors who treat
+> children as standalone REST endpoints that happen to be discovered through a 
parent. Mixins
+> are different: they're explicitly *inline*, share the host's URL namespace, 
and exist to
+> compose with the host. Inheritance is the right model for mixins; isolation 
is the right model
+> for children."
+
+A quick checklist:
+
+| Use a mixin (`@Rest(mixins=...)`) when…                                  | 
Use a child (`@Rest(children=...)`) when…                              |
+|--------------------------------------------------------------------------|------------------------------------------------------------------------|
+| The addon lives inline at the host's URL namespace (`/healthz` next to 
`/items`). | The addon owns its own URL subtree (`/admin/*` under `/`).      |
+| The addon should inherit the host's serializer / guard / hook chain.     | 
The addon should be isolated from the host's resolution chain.         |
+| The addon is a lightweight composer (small set of `@RestOp` methods).    | 
The addon is a heavyweight independent resource (own DB, own lifecycle). |
+| You want per-addon overrides scoped to the addon's endpoints.            | 
You want per-resource isolation between addon and host.                |
+
+Most "I want to add probes / version endpoint / favicon / OpenAPI docs to my 
API" use cases are
+mixins. Most "I want to mount an unrelated REST surface under the same 
servlet" use cases are
+children. The divergence is not deferred — it's the intentional v1 model.
+
+## Guard inheritance security note
+
+Guards inherit by default. This is a deliberate "fail strict" choice. The 
resolved-decisions log
+explains:
+
+> "Surprises around security defaults should err on 'too strict' (mixin 
endpoint accidentally
+> protected) not 'too loose' (mixin endpoint accidentally exposed). A 
`BearerTokenGuard` on the
+> host automatically protects mixin endpoints unless the mixin explicitly opts 
out."
+
+Concretely: if the host declares `@Rest(guards=BearerTokenGuard.class)`, every 
mixin endpoint on
+that host is also protected by `BearerTokenGuard` — even if the mixin's class 
declares no guards
+itself. The only way to expose a mixin endpoint without the host's guard chain 
is to declare
+`@Rest(noInherit={"guards"})` on the mixin's class.
+
+`BasicHealthResource` is the canonical example of a deliberately-unguarded 
mixin: probes
+(`/healthz`, `/readyz`, `/livez`) need to be reachable by the load balancer / 
orchestrator even
+when the rest of the API requires authentication. `BasicHealthResource` 
declares
+`@Rest(noInherit={"guards"})` so its endpoints stay unguarded regardless of 
what guard chain the
+host carries.
+
+The asymmetry — host adds a guard, mixin endpoints become protected unless the 
mixin opts out —
+is intentional. Accidentally protected is recoverable (the user sees a 
401/403, the mixin
+author adds `noInherit`); accidentally exposed is not (the endpoint is 
reachable to attackers
+before anyone notices).
+
+## See also
+
+- [REST Server — Mixins and Multi-Mount 
Paths](/docs/topics/RestServerCompositionMixinsAndPaths)
+  — the broader `@Rest(mixins=...)` / `@Rest(paths=...)` reference. This topic 
page is the
+  sub-context-and-inheritance follow-on.
+- [Child Resources](/docs/topics/ChildResources) — the alternative composition 
primitive when
+  isolation (not inheritance) is what you want.
+- [Health / Readiness / Liveness Probes](/docs/topics/HealthProbes) — the 
canonical
+  `noInherit={"guards"}` mixin in the codebase.
+- [REST Server — Logging and 
Debugging](/docs/topics/RestServerLoggingAndDebugging) —
+  per-mixin `callLogger` / `debugEnablement` override patterns.
+- [REST Server — SVL Variables](/docs/topics/RestServerSvlVariables) — context 
for the
+  `$L{key}` variable referenced from the Messages section.

Reply via email to