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 b5dc7e848e Document @Child host-side seeding of child resources 
(TODO-182)
b5dc7e848e is described below

commit b5dc7e848e9033bb5b5de2dc9275e52047898e8a
Author: James Bognar <[email protected]>
AuthorDate: Fri Jul 24 16:53:12 2026 -0400

    Document @Child host-side seeding of child resources (TODO-182)
    
    Co-authored-by: Cursor <[email protected]>
---
 pages/topics/10.02.02.ChildResources.md | 108 +++++++++++++++++++++++++++++++-
 1 file changed, 107 insertions(+), 1 deletion(-)

diff --git a/pages/topics/10.02.02.ChildResources.md 
b/pages/topics/10.02.02.ChildResources.md
index 1dbfa4f57e..0ee9b0ef79 100644
--- a/pages/topics/10.02.02.ChildResources.md
+++ b/pages/topics/10.02.02.ChildResources.md
@@ -150,7 +150,113 @@ MyRest.builder().lazyChildInit(true).build();
 The first request to a lazy child pays the full construction cost, which can 
be significant for heavyweight children.
 If predictable first-request latency is required, do not opt in — keep the 
default eager behavior.
 
+## Host-side seeding with `@Child` (10.0.0)
+
+Children are deliberately **isolated** from the host's resolution chain (see 
[Mixin-vs-child
+divergence](/docs/topics/RestServerMixinSubContexts#mixin-vs-child-divergence))
 — a child's
+serializers, parsers, guards, call logger, etc. are resolved against the 
child's own `RestContext`
+only, never walked through the parent. `@Rest(childrenDefs=@Child(...))` 
doesn't change that
+isolation; it gives the host an explicit, opt-in way to **seed** a curated set 
of settings onto a
+specific child's otherwise-isolated context, at the point the host declares it:
+
+```java
+@Rest(
+    childrenDefs = @Child(type = AdminResource.class, callLogger = 
StructuredJsonLogger.class, guards = AdminBearerGuard.class)
+)
+public class ApiResource extends BasicRestServlet {
+    @RestGet("/items") public List<Item> items() { ... }
+}
+```
+
+`AdminResource`'s endpoints now log through `StructuredJsonLogger` and require 
`AdminBearerGuard`
+— as declared by the host — without editing `AdminResource` itself and without 
those settings
+leaking into `ApiResource`'s own `/items` resolution chain.
+
+`childrenDefs` is **additive to and coexists with** `children`: bare-class 
entries in `children=`
+and rich `@Child` entries in `childrenDefs=` are discovered together (bare 
entries first, then
+`childrenDefs` entries). A `@Child(type=X.class)` with no seed members is the 
exact equivalent of
+a bare `children=X.class` entry; if both name the same class, the rich entry 
wins (the bare entry
+is upgraded in place) — the same equivalence rule `@Mixin` establishes for 
`mixins`/`mixinDefs`.
+
+### The unifying principle: seeds never remove or override the child's own 
config
+
+Unlike a host `@Mixin` override — which *wins* over the mixin class's own 
declarations — a
+`@Child` seed can only **add to** or **fill gaps in** the child's own 
configuration. It never lets
+the child weaken or discard something the host imposed, and it never masks 
something the child
+explicitly declared for itself. Every seed member falls into exactly one of 
two buckets:
+
+| Bucket | Members | Behavior |
+|---|---|---|
+| **Additive-security** | `guards`, `converters`, `roleGuard`, `rolesDeclared` 
| The host's contribution is added alongside whatever the child declares — the 
child can never remove or weaken it. |
+| **Child-wins scalars** | `callLogger`, `partSerializer`, `partParser`, 
`debug`, `defaultCharset`, `maxInput` | The seed is a fallback/default. If the 
child's own `@Rest` chain explicitly declares the same setting, the child's 
value wins; the seed applies only when the child is silent. |
+
+**Additive-security, list-shaped (`guards`, `converters`) — PREPEND.** The 
host-seeded entries run
+first (outermost), then the child's own declared entries. A seeded guard 
degrades to a plain "set"
+when the child declares no guards of its own, and the child's own 
guards/converters are never
+dropped:
+
+```java
+@Rest(path = "/admin", guards = AdminOwnGuard.class)  // the child's own guard
+public class AdminResource {
+    @RestGet(path = "/threads") public String threads() { ... }
+}
+
+@Rest(childrenDefs = @Child(type = AdminResource.class, guards = 
HostGateGuard.class))
+public class ApiResource extends BasicRestServlet { ... }
+```
+
+A request to `/admin/threads` must satisfy **both** `HostGateGuard` (seeded, 
runs first) and
+`AdminOwnGuard` (the child's own, runs second) — neither is optional.
+
+**Additive-security, role-based (`roleGuard`/`rolesDeclared`) — AND-STACK.** A 
host-seeded
+`roleGuard`/`rolesDeclared` and the child's own explicit value both apply as 
independent, ANDed
+role checks, rather than one overriding the other. If the host seeds 
`roleGuard="admin"` and the
+child declares its own `roleGuard="on-call"`, a request must satisfy **both** 
roles.
+
+**Child-wins scalars — fallback only.** If the child leaves `partSerializer` 
(or any other
+child-wins member) undeclared, the host's seeded value takes effect. If the 
child declares its own
+`@Rest(partSerializer=...)`, the child's value wins outright and the seed is 
ignored for that
+property — same "does this property have an explicit value anywhere 
more-derived?" resolution the
+framework already uses everywhere else.
+
+### Not yet supported: `defaultAccept` / `defaultContentType`
+
+`defaultAccept` and `defaultContentType` are **not** `@Child` seed members. 
They're resolved
+through the framework's shared default-request-headers mechanism 
(`HttpHeaderList.setDefault(...)`),
+which keeps the *first* value set while walking the annotation chain 
least-derived-first —
+independently of `@Child`, this already resolves in favor of the least-derived 
declaration in any
+class hierarchy. Seeding these two through `@Child` would therefore make the 
host's seed win over
+the child's own explicit value, the opposite of the child-wins contract every 
other scalar in this
+bucket honors. Rather than ship that contract violation, they're deferred 
until `HttpHeaderList`'s
+first-wins resolution is revisited.
+
+### `noInherit` interaction
+
+`@Child` itself has no `noInherit()` member — there's no inherited chain to 
cut in an isolated
+context. But a child's own `@Rest(noInherit="<property>")` **does** cut the 
corresponding host
+seed too, for any seedable property, additive-security or child-wins-scalar 
alike:
+
+```java
+@Rest(path = "/admin", noInherit = "guards")  // opts out of the host's seeded 
guard entirely
+public class AdminResource {
+    @RestGet(path = "/threads") public String threads() { ... }
+}
+
+@Rest(childrenDefs = @Child(type = AdminResource.class, guards = 
HostGateGuard.class))
+public class ApiResource extends BasicRestServlet { ... }
+```
+
+With `noInherit = "guards"` on `AdminResource`, `HostGateGuard` is **not** 
applied at all — the
+child stays fully in control, exactly as if the host had never seeded it. This 
falls out of the
+same generic `noInherit` cutoff the framework already applies uniformly to 
every `@Rest` property;
+there's no special-casing for seeded vs. non-seeded values.
+
+### Lazy children
+
+A `@Child` seed survives deferred construction under 
`@Rest(lazyChildren="true")` — a lazily
+materialized child receives its seed on first request exactly as an 
eagerly-built child would.
+
 ## See also
 
 - [REST Server — Children vs Mixins](/docs/topics/RestServerChildrenVsMixins) 
— the same-vs-different matrix for choosing children (isolation) vs mixins 
(inheritance).
-- [REST Server — Mixin Sub-Contexts](/docs/topics/RestServerMixinSubContexts) 
— the inheritance-based alternative composition primitive.
+- [REST Server — Mixin Sub-Contexts](/docs/topics/RestServerMixinSubContexts) 
— the inheritance-based alternative composition primitive, including the 
host-side `@Mixin` override this page's `@Child` section mirrors.

Reply via email to