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 9f3dc5acb2 Measure MCP schema-safety compute budget as thread CPU
time; add view-mixin responseProcessor auto-fold opt-in; FreeMarker
object-wrapper + fluent ConfigItem.setValue
9f3dc5acb2 is described below
commit 9f3dc5acb28d9ddb0b34b4138e3d80d9fbff5240
Author: James Bognar <[email protected]>
AuthorDate: Thu Aug 13 18:04:21 2026 -0400
Measure MCP schema-safety compute budget as thread CPU time; add view-mixin
responseProcessor auto-fold opt-in; FreeMarker object-wrapper + fluent
ConfigItem.setValue
McpSchemaSafety (mcp-v20260728): the tools/call input-schema-validation DoS
budget is now charged against the validating thread's actual CPU time
(ThreadMXBean.getThreadCpuTime) instead of wall-clock, so OS
preemption/scheduling no longer counts against the ~100ms budget. Includes a
support-check with a verbatim wall-clock fallback when thread-CPU timing is
unavailable, correct ns/-1-sentinel handling, and sleep-vs-CPU-burn regression
tests. Fixes an intermittent CI false-trip (-32602 [...]
TODO-358 (juneau-rest-server view mixins): (1) FreemarkerDispatcher's
bridge-default Configuration now installs a
DefaultObjectWrapper(exposeFields=true) so public-field DTOs render instead of
silently resolving to null, with new
exposeFields(boolean)/objectWrapper(ObjectWrapper) builder knobs. (2) New
non-silent, response-processor-scoped
@Rest(mergeResponseProcessorsIntoHost=true) opt-in: a plain
@Rest(mixins=<ViewMixin>.class) now folds the mixin's renderer into the host
chain so t [...]
TODO-320 (juneau-sc-server): ConfigItem.setValue(String) is now a fluent
self-returning setter, matching the repo-wide convention (the lone void-setter
outlier); source-compatible, no reflective contract broken.
Docs: release notes for both behavioral changes, plus FreeMarker/JSP
view-support and mixin-subcontexts topic updates.
---
pages/release-notes/10.0.0.md | 19 +++++
pages/topics/10.08.RestServerMixinSubContexts.md | 28 ++++++
pages/topics/10.24.JspViewSupport.md | 5 +-
pages/topics/10.27.FreemarkerViewSupport.md | 104 ++++++++++++++++++++---
4 files changed, 141 insertions(+), 15 deletions(-)
diff --git a/pages/release-notes/10.0.0.md b/pages/release-notes/10.0.0.md
index d1980a4f5c..a51e835064 100644
--- a/pages/release-notes/10.0.0.md
+++ b/pages/release-notes/10.0.0.md
@@ -393,6 +393,21 @@ public class ApiResource extends BasicRestServlet { ... }
Internally this also normalized mixin discovery (one `ResolvedMixin` carrier
for both bare and rich forms) and replaced the `RestContext.Args` boolean
`mixinContext` flag with a typed `ContextKind` discriminator
(`Root`/`Child`/`Mixin`). See [Host-side overrides with
`@Mixin`](/docs/topics/RestServerMixinSubContexts#host-side-overrides-with-mixin-1000).
+### Mixin-declared response-processor fold —
`@Rest(mergeResponseProcessorsIntoHost=true)`
+
+By default a mixin's `responseProcessors` apply only to the mixin's own
endpoints — a host's own `@RestOp` methods see only the host's chain. The
host-side `@Mixin(mergeIntoHost=true)` directive can fold *all* of a mixin's
list-shaped attributes into the host, but it must be spelled out at every
use-site. New in 10.0.0, a mixin class can declare
`@Rest(mergeResponseProcessorsIntoHost=true)` on **itself**, so that a plain
`@Rest(mixins=ThatMixin.class)` reference automatically folds **onl [...]
+
+```java
+@Rest(responseProcessors=FreemarkerViewRenderer.class,
mergeResponseProcessorsIntoHost=true)
+public class FreemarkerMixin { ... }
+```
+
+- **Opt-in and non-silent.** Defaults to `false`; only a mixin that declares
it changes behavior. Mixins that don't keep today's isolated scoping exactly —
no change to the default for arbitrary existing mixins.
+- **Response-processor-scoped.** Folds only `responseProcessors` — never
`guards`, `serializers`, or any other list-shaped attribute (the narrower,
mixin-declared counterpart to the host-side, fold-everything
`@Mixin(mergeIntoHost=true)`). Same-class de-duplication applies.
+- **View mixins opt in.** All four view-renderer mixins — `FreemarkerMixin`,
`JspMixin`, `MustacheMixin`, `ThymeleafMixin` — now declare it, so a bare
`@Rest(mixins=FreemarkerMixin.class)` (etc.) renders the host's own `View`
returns through the engine with no `mergeIntoHost` or explicit
`responseProcessors=` ceremony.
+
+See [REST Server — Mixin
Sub-Contexts](/docs/topics/RestServerMixinSubContexts#folding-a-mixins-contributions-into-the-host)
and [FreeMarker View Support](/docs/topics/FreemarkerViewSupport).
+
### Host-side child seeding — `@Rest(childrenDefs=@Child(...))`
Children are deliberately **isolated** from the host's resolution chain — a
child's serializers, parsers, guards, call logger, etc. resolve against its own
`RestContext` only, never inherited from the parent. New in 10.0.0, the
`@Rest(childrenDefs=@Child(...))` attribute lets a host **seed** a curated set
of settings onto a routed child's otherwise-isolated context, without editing
the child class — the child-resource analog of the
`@Rest(mixinDefs=@Mixin(...))` attribute above:
@@ -1032,6 +1047,10 @@ See
[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp#key-management-key
See
[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp#replay-protection-replaycache-opt-in)
for the full topic.
+### MCP `2026-07-28` — schema-safety compute budget measured as thread CPU time
+
+`2026-07-28`-only. The bounded, no-fetch `tools/call` input-schema validation
guard enforces a compute budget so a pathological schema (for example a
catastrophically-backtracking `pattern`) can't hang the request thread —
tripping `-32602` ("Tool input schema validation exceeded 100 ms") instead.
That budget is now charged against the **validating thread's actual CPU time**
rather than wall-clock time: OS scheduler preemption and thread-pool queueing
no longer count against it, only rea [...]
+
### MCP `2026-07-28` — OAuth 2.1 authorization: client acquisition + server
resource server (TODO-312f)
`2026-07-28`-only, and **off by default** — an endpoint that doesn't opt in
behaves exactly as before. 10.0.0 lands the OAuth 2.1 / MCP authorization
baseline in three slices: client-side token acquisition (F1), a server-side
resource-server (F2), and dynamic client registration + authorization hardening
(F3). Together they let a Juneau MCP client authenticate to, and a Juneau MCP
endpoint protect, a `2026-07-28` MCP resource end to end, covering the MCP
authorization SEPs at a baseline [...]
diff --git a/pages/topics/10.08.RestServerMixinSubContexts.md
b/pages/topics/10.08.RestServerMixinSubContexts.md
index 2be916d6fd..141f217599 100644
--- a/pages/topics/10.08.RestServerMixinSubContexts.md
+++ b/pages/topics/10.08.RestServerMixinSubContexts.md
@@ -339,6 +339,34 @@ wins (the bare entry is upgraded in place).
`messages`,
`defaultRequestHeaders`/`defaultResponseHeaders`/`defaultRequestAttributes`,
`produces`/`consumes`/`defaultAccept`/`defaultContentType`/`defaultCharset`,
and `maxInput`.
+### Folding a mixin's contributions into the host
+
+The override slots above apply the *host's* values to the *mixin's* endpoints.
The reverse
+direction — appending the *mixin's* own list-shaped `@Rest` contributions into
the *host's* own
+chain, so the host's own `@RestOp` methods pick them up too — is available via
two opt-in
+directives. Both are strictly opt-in and non-silent: a mixin that declares
neither keeps today's
+default scoping exactly (host endpoints see only the host's chain).
+
+| Directive | Declared on | Folds | Use when |
+|---|---|---|---|
+| `@Mixin(mergeIntoHost=true)` | the **host** (via `mixinDefs`) | **all** of
the mixin's list-shaped `@Rest` attributes (serializers, parsers, encoders,
converters, guards, responseProcessors, restOpArgs, default headers/attributes,
produces, consumes) | the host wants to adopt a mixin's full list-shaped
contribution surface into its own endpoints |
+| `@Rest(mergeResponseProcessorsIntoHost=true)` | the **mixin class** itself |
**only** the mixin's `responseProcessors` | a mixin (typically a view-renderer
mixin) wants "declare the mixin → it just works" for the host's own returns,
under a plain `mixins=` reference, without folding anything but its renderer |
+
+```java
+// Host-side, broad: fold ALL of AdminResource's list-shaped attributes into
the host chain.
+@Rest(mixinDefs = @Mixin(type = AdminResource.class, mergeIntoHost = true))
+public class ApiResource extends BasicRestServlet { ... }
+
+// Mixin-declared, narrow: a view mixin folds ONLY its renderer into any
plain-mixins= host.
+@Rest(responseProcessors = FreemarkerViewRenderer.class,
mergeResponseProcessorsIntoHost = true)
+public class FreemarkerMixin { ... }
+```
+
+`mergeResponseProcessorsIntoHost` is what makes a bare
`@Rest(mixins=FreemarkerMixin.class)` render
+the host's own `FreemarkerView` returns (see [FreeMarker View
Support](/docs/topics/FreemarkerViewSupport)
+and the sibling JSP / Thymeleaf / Mustache bridges, which all declare it).
Same-class de-duplication
+applies to both directives, so a response processor the host already declares
is never added twice.
+
### Precedence and `noInherit`
A host `@Mixin` override resolves as if it were declared on the mixin class
itself, but at the
diff --git a/pages/topics/10.24.JspViewSupport.md
b/pages/topics/10.24.JspViewSupport.md
index 0d7a731555..f3ff63bb4d 100644
--- a/pages/topics/10.24.JspViewSupport.md
+++ b/pages/topics/10.24.JspViewSupport.md
@@ -212,7 +212,10 @@ known constraints that aren't bridge-module bugs but worth
documenting up front:
[`ViewRenderer`](/site/apidocs/org/apache/juneau/rest/server/view/ViewRenderer.html)
and is
automatically positioned before `SerializedPojoProcessor` by the
`ResponseProcessorList`
partition pass. A `@RestOp` method returning `JspView` reaches the renderer
without any
-extra configuration — the mixin registers the renderer and the framework
ensures ordering.
+extra configuration — `JspMixin` declares
+`@Rest(mergeResponseProcessorsIntoHost=true)`, so a plain
`@Rest(mixins=JspMixin.class)` folds
+`JspViewRenderer` into the host's own chain and the framework ensures
ordering. See
+[REST Server — Mixin Sub-Contexts](/docs/topics/RestServerMixinSubContexts)
for the opt-in model.
## Multiple base paths
diff --git a/pages/topics/10.27.FreemarkerViewSupport.md
b/pages/topics/10.27.FreemarkerViewSupport.md
index cacdd513f5..cfad0cc30e 100644
--- a/pages/topics/10.27.FreemarkerViewSupport.md
+++ b/pages/topics/10.27.FreemarkerViewSupport.md
@@ -33,7 +33,7 @@ matching Juneau's own license stance.
| Class | Role |
|---|---|
| [`View`](/site/apidocs/org/apache/juneau/rest/server/view/View.html) (in
`juneau-rest-server` core) | Engine-agnostic contract: `getTemplateName()`,
`getAttributes()`, `getResponseHeaders()`. |
-|
[`FreemarkerMixin`](/site/apidocs/org/apache/juneau/rest/server/view/freemarker/FreemarkerMixin.html)
| Mixin. Mounts `/freemarker/*` for raw `.ftl` / `.ftlh` template requests;
registers `FreemarkerViewRenderer` on the response-processor chain. Builder:
`basePath(String)` (default `/`), `templateSuffix(String)` (default `""`),
`cacheTemplates(boolean)` (default `true`). Builder implements the shared
[`ViewMixinBuilder`](/site/apidocs/org/apache/juneau/rest/server/view/ViewMixinBuilder
[...]
+|
[`FreemarkerMixin`](/site/apidocs/org/apache/juneau/rest/server/view/freemarker/FreemarkerMixin.html)
| Mixin. Mounts `/freemarker/*` for raw `.ftl` / `.ftlh` template requests;
registers `FreemarkerViewRenderer` on the response-processor chain. Builder:
`basePath(String)` (default `/`), `templateSuffix(String)` (default `""`),
`cacheTemplates(boolean)` (default `true`), `exposeFields(boolean)` (default
`true`), `objectWrapper(ObjectWrapper)` (default `null` — bridge builds its
own). B [...]
|
[`FreemarkerView`](/site/apidocs/org/apache/juneau/rest/server/view/freemarker/FreemarkerView.html)
| `View` implementation. Immutable; fluent:
`FreemarkerView.of("hello.ftlh").attr("name", name).header("Cache-Control",
"no-store")`. |
|
[`FreemarkerViewRenderer`](/site/apidocs/org/apache/juneau/rest/server/view/freemarker/FreemarkerViewRenderer.html)
| `ResponseProcessor` that detects `FreemarkerView` returns and asks the
configured `freemarker.template.Configuration` to
`getTemplate(name).process(dataModel, writer)` directly onto the response
writer. |
@@ -70,6 +70,56 @@ encodings / output formats you need.
See https://juneau.apache.org/docs/topics/FreemarkerViewSupport for the full
matrix.
```
+### Public-field DTOs and the default object wrapper
+
+FreeMarker's own version-default `ObjectWrapper` exposes only JavaBean getters
to templates —
+a view-model bean written as a simple DTO with public fields and no getters
resolves every
+`${bean.field}` reference to `null`/missing, **silently**. That silence is the
dangerous part: a
+template using an `!'default'` fallback (`${bean.field!'—'}`) renders the
fallback with no error
+at all, and a bare `${bean.field}` with no fallback throws at render time
instead of failing at
+compile/wiring time.
+
+The bridge-default `Configuration` avoids this trap: it sets a
`DefaultObjectWrapper` with
+`exposeFields=true`, so public-field DTOs render their field values out of the
box — matching
+how Juneau's own marshalling is comfortable with public-field beans elsewhere.
For example:
+
+```java
+public class Coordinate {
+ public double lat;
+ public double lon;
+}
+```
+
+```html
+<p>Lat/Lon: ${coord.lat}, ${coord.lon}</p>
+```
+
+renders the actual field values with **no getters required** and no extra
configuration.
+
+Two builder knobs let you opt out or take full control, both scoped to the
**bridge-default**
+`Configuration` only (a user-supplied `@Bean Configuration` is used as-is,
un-modified):
+
+```java
+@Bean
+FreemarkerMixin freemarker() {
+ return FreemarkerMixin.create()
+ .basePath("/templates/")
+ .templateSuffix(".ftlh")
+ .exposeFields(false) // restore FreeMarker's own getter-only default
+ .build();
+}
+```
+
+```java
+@Bean
+FreemarkerMixin freemarker() {
+ return FreemarkerMixin.create()
+ .basePath("/templates/")
+ .objectWrapper(myCustomBeansWrapper) // full control; overrides
exposeFields(...) entirely
+ .build();
+}
+```
+
## Hello-world
### Maven
@@ -147,10 +197,26 @@ data model, and asks the resolved `Template` to render
`/templates/hello.ftlh` d
the response writer. The `.ftlh` suffix is appended idempotently because the
builder
declared it.
+> **Why does the bare `mixins=FreemarkerMixin.class` shorthand render the
host's own
+> `FreemarkerView` returns?** By default a mixin's
`@Rest(responseProcessors=...)` declaration
+> applies only to the *mixin's own* endpoints (here, the `/freemarker/*`
raw-template mount) — a
+> host's own `@RestOp` methods see only the host's own response-processor
chain. `FreemarkerMixin`
+> opts out of that default: it declares
`@Rest(mergeResponseProcessorsIntoHost=true)` on its own
+> class, so a plain `@Rest(mixins=FreemarkerMixin.class)` folds
`FreemarkerViewRenderer` into the
+> host's own chain automatically and the `hello(...)` method above renders
instead of being
+> bean-serialized. The opt-in is **per-mixin, non-silent, and
response-processor-scoped**: only a
+> mixin that declares it changes behavior, and only its renderer is folded —
never `guards`,
+> `serializers`, or any other list-shaped attribute. If you want to fold *all*
of a mixin's
+> list-shaped `@Rest` attributes (not just the renderer), the host-side
+> `mixinDefs=@Mixin(type=FreemarkerMixin.class, mergeIntoHost=true)` form
still does that. See
+> [REST Server — Composition](/docs/topics/RestServerComposition) for the full
`@Mixin` /
+> `mixinDefs` / opt-in model.
+
### Typed handler integration
-To use `FreemarkerView` from a host-class `@RestGet` *without* adopting the
full mixin, add
-`FreemarkerViewRenderer.class` directly to the host's `responseProcessors`:
+A fully explicit alternative — useful when you'd rather not rely on the
mixin's auto-fold opt-in,
+or when the host doesn't declare the mixin at all — is to list
`FreemarkerViewRenderer.class`
+directly in the host's own `responseProcessors`:
```java
@Rest(responseProcessors={FreemarkerViewRenderer.class})
@@ -162,10 +228,12 @@ public class AppResource extends BasicRestServlet {
}
```
-The `ResponseProcessorList` partition pass (added in 9.5.0) automatically
repositions
-`FreemarkerViewRenderer` to run before `SerializedPojoProcessor`. Because
FreeMarker renders
-directly to a `Writer` with zero servlet-container dependencies, attribute
interpolation
-(`${name}` → `"Alice"`) works under MockRest — verified end-to-end by
+Once `FreemarkerViewRenderer` is in a chain (whichever of the two ways above
got it there), the
+`ResponseProcessorList` partition pass (added in 9.5.0) automatically
repositions it to run
+before `SerializedPojoProcessor`, so the `FreemarkerView` return is dispatched
to the FreeMarker
+engine rather than bean-serialized. Because FreeMarker renders directly to a
`Writer` with zero
+servlet-container dependencies, attribute interpolation (`${name}` →
`"Alice"`) works under
+MockRest — verified end-to-end by
`FreemarkerView_TypedHandler_Test#a04_renderInterpolatesAttributesFromTypedHandler`.
### REST resource — raw-template mount
@@ -254,10 +322,17 @@ is bypassed entirely when the starter is on the classpath.
`FreemarkerViewRenderer` implements
[`ViewRenderer`](/site/apidocs/org/apache/juneau/rest/server/view/ViewRenderer.html)
and is
automatically positioned before `SerializedPojoProcessor` by the
`ResponseProcessorList`
-partition pass. A `@RestOp` method returning `FreemarkerView` reaches the
renderer
-without any extra configuration — the mixin registers the renderer and the
framework ensures
-ordering. MockRest-level typed-handler integration is verified by
-`FreemarkerView_TypedHandler_Test` (attribute interpolation asserted
end-to-end).
+partition pass, **once it's present in the relevant chain**. That's a distinct
question from
+*whether* the renderer is present in the host's own chain in the first place.
Because
+`FreemarkerMixin` declares `@Rest(mergeResponseProcessorsIntoHost=true)`, a
bare
+`@Rest(mixins=FreemarkerMixin.class)` **does** now put the renderer into the
host's own chain for
+the host's own `@RestOp` methods (see the "Why does the bare `mixins=`
shorthand render..." callout
+above). The host-side `mixinDefs=@Mixin(type=FreemarkerMixin.class,
mergeIntoHost=true)` form and
+an explicit `@Rest(responseProcessors={FreemarkerViewRenderer.class})` on the
host remain
+supported. In every case, once the renderer is in the host's chain, the
partition pass handles
+ordering automatically — no further configuration needed. MockRest-level
typed-handler integration
+(renderer-vs-serializer dispatch) is verified by
`FreemarkerView_TypedHandler_Test` (attribute
+interpolation asserted end-to-end).
## Multiple base paths
@@ -306,9 +381,10 @@
anyEngineBuilder.basePath("/templates/").cacheTemplates(false);
## Limitations and out-of-scope
- **No bridge-default `Configuration` mutation for user beans.** The
- `cacheTemplates(boolean)` builder knob only configures the *bridge-default*
- `Configuration`. If you provide your own `@Bean Configuration` (Spring Boot
autoconfig
- counts), configure caching directly on that bean — the bridge does not
mutate it.
+ `cacheTemplates(boolean)`, `exposeFields(boolean)`, and
`objectWrapper(ObjectWrapper)`
+ builder knobs only configure the *bridge-default* `Configuration`. If you
provide your own
+ `@Bean Configuration` (Spring Boot autoconfig counts), configure field
exposure / caching
+ directly on that bean — the bridge does not mutate it.
- **`.ftlh` is the recommended HTML default.** FreeMarker's auto-escape
selection is by
file extension. For HTML responses prefer `.ftlh`; using `.ftl` for HTML
leaves variable
references un-escaped and risks XSS regressions when an attribute binding is
later