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 84f26d4e66 Add @Mixin host-side per-mixin overrides and the
Actuator-style management surface
84f26d4e66 is described below
commit 84f26d4e667f941d0cc0506a513d4e0f72422630
Author: James Bognar <[email protected]>
AuthorDate: Thu Jun 18 11:33:14 2026 -0400
Add @Mixin host-side per-mixin overrides and the Actuator-style management
surface
---
pages/release-notes/10.0.0.md | 32 +++++
pages/topics/10.03.03.ChildResources.md | 5 +
pages/topics/10.08.RestServerComposition.md | 6 +
pages/topics/10.09.RestServerMixinSubContexts.md | 74 ++++++++++
.../topics/10.10.05.RestServerChildrenVsMixins.md | 71 +++++++++
pages/topics/16.12.ManagementSurface.md | 158 +++++++++++++++++++++
sidebars.ts | 10 ++
7 files changed, 356 insertions(+)
diff --git a/pages/release-notes/10.0.0.md b/pages/release-notes/10.0.0.md
index e7e72c4530..b7041586fd 100644
--- a/pages/release-notes/10.0.0.md
+++ b/pages/release-notes/10.0.0.md
@@ -193,6 +193,38 @@ See the new [Content Security
Policy](/docs/topics/ContentSecurityPolicy) topic
This feature was inspired by an earlier proposal from **Gary Gregory**
([@garydgregory](https://github.com/garydgregory), PR #57); the implementation
shipped here is a fresh one built against the current 10.0 APIs.
+#### Actuator-style management surface
+
+Juneau 10.0 adds a coherent, discoverable **Actuator-style management
surface** while keeping Juneau's a-la-carte composability. Every endpoint ships
in two independently-mountable flavors — a composable `*Mixin` and a routed
`*Resource` sharing one worker — and a convenience `BasicActuatorGroup`
assembles them under one configurable path prefix (default `/actuator`,
overridable via the `juneau.actuator.path` system property).
+
+New endpoints:
+
+- **`/info`** (`juneau-microservice`,
`org.apache.juneau.microservice.management`) — renders the running
application's `ManifestFile` main attributes (build/version/git metadata).
Degrades to `{}` when no manifest is registered. Build-time git/version
stamping goes in the consumer's application-jar build (a `git-commit-id` +
`maven-jar-plugin` recipe is in the topic page).
+- **`/loggers`** (`juneau-rest-server`,
`org.apache.juneau.rest.server.management`) — read all `java.util.logging`
levels and (deny-by-default) set a level at runtime. **JUL-only in v1**;
non-JUL backends (SLF4J/Logback/Log4j2) are tracked as a follow-on.
+- **`/metrics`** (`juneau-rest-server-metrics-micrometer`) — renders a
Prometheus scrape from a consumer-provided Micrometer `MeterRegistry` resolved
from the bean store; degrades to `501` when no scrapeable registry is present.
Never auto-registers a default registry.
+- **`/threaddump` + `/heapdump`** (`juneau-rest-server`) — server-agnostic
`ThreadMXBean` thread dump and HotSpot `.hprof` heap dump, both
**deny-by-default** on security grounds (opt in via `DumpsSettings`).
+
+Exposure is **on-by-default-with-gating**: non-sensitive reads are exposed;
mutating/sensitive operations (`/loggers` set-level, the dumps) are
deny-by-default behind explicit opt-in settings beans (`LoggersSettings`,
`DumpsSettings`). No auth provider is auto-wired — the consumer supplies their
own guard.
+
+See the new [Management Surface](/docs/topics/ManagementSurface) topic page
for the endpoint catalog, the `BasicActuatorGroup` one-liner, the exposure
policy, and the manifest-stamping recipe.
+
+#### Host-side mixin overrides — `@Rest(mixinDefs=@Mixin(...))`
+
+`@Rest(mixins=...)` (since 9.5.0) lets a mixin class declare its own `@Rest`
settings that apply only to its endpoints — but the composition decision often
belongs to the *host*. New in 10.0.0, the `@Rest(mixinDefs=@Mixin(...))`
attribute lets a host declare a mixin **and** override selected `@Rest`-level
settings for that mixin's endpoints, in one place, without editing or
subclassing the mixin:
+
+```java
+@Rest(mixinDefs=@Mixin(type=AdminResource.class, guards=AdminGuard.class))
+public class ApiResource extends BasicRestServlet { ... }
+```
+
+- **Additive / non-breaking.** `mixinDefs` coexists with the existing
`mixins=Class<?>[]`; a `@Mixin(type=X.class)` with no overrides is exactly
equivalent to a bare `mixins=X.class` entry, and existing usages are untouched.
+- **Override slots** mirror the composition-shaped `@Rest` attributes:
`guards`, `roleGuard`/`rolesDeclared`, `converters`, `encoders`,
`serializers`/`parsers`, `responseProcessors`, `restOpArgs`, `callLogger`,
`partSerializer`/`partParser`, `debug`, `messages`, the default
request/response headers + attributes,
`produces`/`consumes`/`defaultAccept`/`defaultContentType`/`defaultCharset`,
and `maxInput`.
+- **Precedence:** the host override layers on top of the inherited host chain
(list-shaped props append, replace-shaped props win) and wins over the mixin
class's own declaration. `@Mixin` carries its own `noInherit` (unioned with the
mixin class's `@Rest(noInherit=...)`) so the host override is authoritative but
overridable by `noInherit`.
+- **Host-chosen re-mount:** `@Mixin(path=...)`/`paths={...}` re-mount the
mixin's endpoints under host-chosen prefix(es), reusing the existing
`pathToken` normalization.
+- **Transitive mixins** are overridden only when separately named.
+
+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).
+
### juneau-marshall
#### Token-Streaming and Record-Streaming API
diff --git a/pages/topics/10.03.03.ChildResources.md
b/pages/topics/10.03.03.ChildResources.md
index 54e838417c..8913b7a279 100644
--- a/pages/topics/10.03.03.ChildResources.md
+++ b/pages/topics/10.03.03.ChildResources.md
@@ -149,3 +149,8 @@ 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.
+
+## 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.
diff --git a/pages/topics/10.08.RestServerComposition.md
b/pages/topics/10.08.RestServerComposition.md
index dd570f725a..f8669a83a0 100644
--- a/pages/topics/10.08.RestServerComposition.md
+++ b/pages/topics/10.08.RestServerComposition.md
@@ -13,6 +13,12 @@ Juneau REST servers ship two complementary composition /
mounting primitives on
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(mixinDefs=@Mixin[])`** (10.0.0) — *host-side rich mixin
composition.* The override form of
+ `mixins`: each `@Mixin(type=X.class, ...)` names a mixin **and** lets the
host override selected
+ `@Rest` settings (guards, serializers, default headers, mount `path`, …) for
that mixin's endpoints
+ without editing or subclassing the mixin. Coexists with bare `mixins=`; a
`@Mixin(type=X.class)`
+ with no overrides equals a bare `mixins=X.class` entry. See
+ [Host-side overrides with
`@Mixin`](/docs/topics/RestServerMixinSubContexts#host-side-overrides-with-mixin-1000).
- **`@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
diff --git a/pages/topics/10.09.RestServerMixinSubContexts.md
b/pages/topics/10.09.RestServerMixinSubContexts.md
index d13041c9ed..24c634c927 100644
--- a/pages/topics/10.09.RestServerMixinSubContexts.md
+++ b/pages/topics/10.09.RestServerMixinSubContexts.md
@@ -305,6 +305,78 @@ would add `noInherit={"callLogger"}` (though for
callLogger and the other replac
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).
+## Host-side overrides with `@Mixin` (10.0.0)
+
+The override above lives on the *mixin* class. But the composition decision —
"for *this* import,
+also apply `AdminGuard`" — often belongs to the *host*, where the mixin is
being assembled. Editing
+the mixin class to add a guard couples it to one host; subclassing it just to
change a setting is
+boilerplate. **10.0.0 adds `@Rest(mixinDefs=@Mixin(...))`** — a host-side rich
mixin declaration that
+names the mixin **and** overrides selected `@Rest`-level settings for that
mixin's endpoints, in one
+place, without touching the mixin class:
+
+```java
+@Rest(
+ mixinDefs = @Mixin(type = AdminResource.class, guards = AdminGuard.class)
+)
+public class ApiResource extends BasicRestServlet {
+ @RestGet("/items") public List<Item> items() { ... }
+}
+```
+
+`AdminGuard` now protects `AdminResource`'s endpoints as declared by the host
— `AdminResource`
+itself declares nothing.
+
+`mixinDefs` is **additive to and coexists with** `mixins`: bare-class entries
in `mixins=` and rich
+`@Mixin` entries in `mixinDefs=` are discovered together. A
`@Mixin(type=X.class)` with no overrides
+is exactly equivalent to a bare `mixins=X.class` entry; if both name the same
class, the rich entry
+wins (the bare entry is upgraded in place).
+
+### Override slots
+
+`@Mixin` exposes override slots mirroring the composition-shaped `@Rest`
attributes:
+`guards`, `roleGuard`/`rolesDeclared`, `converters`, `encoders`,
`serializers`/`parsers`,
+`responseProcessors`, `restOpArgs`, `callLogger`,
`partSerializer`/`partParser`, `debug`,
+`messages`,
`defaultRequestHeaders`/`defaultResponseHeaders`/`defaultRequestAttributes`,
+`produces`/`consumes`/`defaultAccept`/`defaultContentType`/`defaultCharset`,
and `maxInput`.
+
+### Precedence and `noInherit`
+
+A host `@Mixin` override resolves as if it were declared on the mixin class
itself, but at the
+*most-derived* position — so it layers on top of the inherited host chain
(list-shaped props append
+after it; replace-shaped props take the override as the winning value), and it
also wins over the
+mixin class's own same-property declaration.
+
+`@Mixin` carries its **own `noInherit`** attribute, which cuts the host→mixin
inheritance walk for
+the named properties — unioned with the mixin class's own
`@Rest(noInherit=...)`. So the host
+override is authoritative *but overridable by the `@Mixin`'s own `noInherit`
rules*:
+
+```java
+// AdminGuard applies; the host's own guard chain is NOT inherited onto
AdminResource's endpoints.
+@Rest(
+ guards = HostGuard.class,
+ mixinDefs = @Mixin(type = AdminResource.class, guards = AdminGuard.class,
noInherit = "guards")
+)
+public class ApiResource extends BasicRestServlet { ... }
+```
+
+### Host-chosen re-mount (`path`/`paths`)
+
+`@Mixin` can also re-mount the mixin's endpoints under host-chosen prefix(es)
via `path`/`paths`,
+reusing the same `pathToken` normalization as the SVL-configurable bundled
mixins (leading/trailing
+slashes and a trailing `/*` collapse to one clean prefix):
+
+```java
+// AdminResource's /threads and /heap endpoints mount at /admin/threads and
/admin/heap.
+@Rest(mixinDefs = @Mixin(type = AdminResource.class, path = "/admin"))
+public class ApiResource extends BasicRestServlet { ... }
+```
+
+### Transitive mixins
+
+A host override applies only to the **directly-named** mixin. If
`AdminResource` transitively pulls
+in another mixin, that transitive mixin is overridden only if the host names
it separately in its own
+`@Mixin`.
+
## `Messages.chain(...)` as the public seam
`@Rest(messages=...)` doesn't ride the standard
`getRestAnnotationsForProperty(...)` walk —
@@ -396,6 +468,8 @@ before anyone notices).
sub-context-and-inheritance follow-on.
- [Child Resources](/docs/topics/ChildResources) — the alternative composition
primitive when
isolation (not inheritance) is what you want.
+- [REST Server — Children vs Mixins](/docs/topics/RestServerChildrenVsMixins)
— the full
+ same-vs-different matrix for choosing between the two.
- [Health / Readiness / Liveness Probes](/docs/topics/HealthProbes) — the
canonical
`noInherit={"guards"}` mixin in the codebase.
- [REST Server — Logging and
Debugging](/docs/topics/RestServerLoggingAndDebugging) —
diff --git a/pages/topics/10.10.05.RestServerChildrenVsMixins.md
b/pages/topics/10.10.05.RestServerChildrenVsMixins.md
new file mode 100644
index 0000000000..97d709494b
--- /dev/null
+++ b/pages/topics/10.10.05.RestServerChildrenVsMixins.md
@@ -0,0 +1,71 @@
+---
+title: "REST Server — Children vs Mixins"
+slug: RestServerChildrenVsMixins
+---
+
+Juneau offers two ways to compose one resource's operations into another:
+[`@Rest(children=...)`](/docs/topics/ChildResources) and
+[`@Rest(mixins=...)`](/docs/topics/RestServerCompositionMixinsAndPaths) (plus
its host-side override
+form
[`@Rest(mixinDefs=@Mixin(...))`](/docs/topics/RestServerMixinSubContexts#host-side-overrides-with-mixin-1000)).
+They look superficially similar — both pull a second class's `@RestOp` methods
into a parent — but
+they are **opposite designs**, and picking the wrong one causes subtle routing
and configuration
+surprises.
+
+The one-line rule:
+
+> **Mixins are *inline* — they share the host's URL namespace and inherit its
configuration. Children
+> are *isolated* — they mount at their own URL prefix and resolve
configuration independently.**
+
+This page is the canonical "which one do I reach for" reference.
+
+## Decision guide
+
+- **Reach for a mixin** when the second class is an *inline* extension of the
host: convention
+ endpoints (health, info, swagger, favicon), cross-cutting ops that should
share the host's
+ serializers/guards/messages, or anything you'd otherwise copy-paste into the
host class. Use
+ `@Mixin(...)` when the *host* wants to override a setting (e.g. guard) for
that mixin's endpoints.
+- **Reach for a child** when the second class is a *standalone* sub-resource
with its own URL subtree
+ and its own configuration lifecycle — often a separately-deployable REST
endpoint that just happens
+ to be discovered through a parent.
+
+## Same vs. different matrix
+
+| Axis | Mixin (`@Rest(mixins=)` / `@Rest(mixinDefs=@Mixin)`) | Child
(`@Rest(children=)`) |
+|---|---|---|
+| **Path composition** | Op merges into the host's operation table and
resolves at its *own* `@RestOp(path)` under the host namespace — no
auto-inserted segment. (A host `@Mixin(path=...)` can re-mount the mixin's ops
under a chosen prefix.) | Child contributes a path **segment** from the child's
`@Rest(path)`; the request is re-dispatched to the child with a trimmed path
remainder. |
+| **`RestContext`** | Own sub-context, **parent-linked** to the host
(`isMixinContext`). | Own context, **isolated** (no parent walk). |
+| **Config inheritance** (serializers, parsers, guards,
`roleGuard`/`rolesDeclared`, converters, encoders, responseProcessors,
restOpArgs; callLogger, partSerializer/partParser, debug; messages) |
**Inherited** from the host: list-shaped props = host chain then mixin
appended; replace-shaped props = mixin value wins else host inherited;
`messages` = mixin bundle chained under the host's. | **Isolated**: the child
resolves each property from its own `@Rest` only; nothing inherits from the
[...]
+| **`noInherit`** | Meaningful — cuts the host→mixin walk per property (mixin
class's `@Rest(noInherit=...)` and/or the host `@Mixin(noInherit=...)`,
unioned). | Not applicable — there is no inherited chain to cut. |
+| **Host-side override** | `@Rest(mixinDefs=@Mixin(type=X, guards=..., ...))`
lets the host override settings for the mixin's endpoints. | No equivalent
today (a future `@Child` is parked, not shipped). |
+| **Operation discovery / collision** | Single merged op table; **host wins**
over mixin on path+method collision; mixin-vs-mixin resolved by `mixins=`
declaration order. | Separate `RestChildren` registry matched by path prefix;
child ops never merge into the host table. |
+| **Request routing / `getPathInfo`** | Mixin op sees the host request
**unchanged** (same servletPath/pathInfo). | Child sees a **rewritten**
servletPath/pathInfo (the matched segment is consumed). |
+| **Matching order** | Ops (host + mixin) matched as one table. | **Children
are matched before** the host's own ops. |
+| **`BeanStore` / DI** | Mixin sub-context's bean store is parent-linked to
the host's (inherits host beans). | Child's bean store is independent (resolves
its own beans). |
+| **Swagger / OpenAPI** | Mixin ops appear on the **host's** generated
Swagger. | Child has its **own** Swagger subtree. |
+| **Instance identity** | Separate instance from the host; mixin ops run on
the mixin instance within the host's request flow. | Separate instance; child
runs its own request flow. |
+| **Lifecycle hooks**
(`@RestStartCall`/`@RestPreCall`/`@RestPostCall`/`@RestEndCall`/`@RestDestroy`)
| **Dual-fire** host-then-mixin for mixin-endpoint requests. | Child runs **its
own** hooks only. |
+| **Transitive composition** | Nested mixins parent-link **flat to the host**
(not chained); a mixin's `@Rest(mixins=B)` discovers `B` at the host level. |
Children nest as a normal resource tree (each child can have its own children).
|
+
+## Why the divergence is intentional
+
+From the resolved-decisions log on the work that drove the mixin sub-context
model:
+
+> "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."
+
+Because the resolution models are opposite, the per-property mixin precedence
(the host `@Mixin`
+override → mixin class's `@Rest` → inherited host chain, gated by `noInherit`)
has **no child
+analog** — a child neither inherits a chain nor honors `noInherit`. A future
`@Rest(childrenDefs=@Child(...))`
+would *seed* settings onto an isolated child rather than *override* an
inherited chain; it is parked,
+not shipped.
+
+## See also
+
+- [REST Server — Mixins and Multi-Mount
Paths](/docs/topics/RestServerCompositionMixinsAndPaths)
+- [REST Server — Mixin Sub-Contexts](/docs/topics/RestServerMixinSubContexts)
— the full inheritance model and `@Mixin` host-side overrides
+- [REST Server — Standalone vs Mixin
Resources](/docs/topics/RestServerStandaloneVsMixin)
+- [Child Resources](/docs/topics/ChildResources)
diff --git a/pages/topics/16.12.ManagementSurface.md
b/pages/topics/16.12.ManagementSurface.md
new file mode 100644
index 0000000000..b1628920c4
--- /dev/null
+++ b/pages/topics/16.12.ManagementSurface.md
@@ -0,0 +1,158 @@
+---
+title: "Management Surface (Actuator-style endpoints)"
+slug: ManagementSurface
+---
+
+> **See also:** [Health / Readiness / Liveness
Probes](/docs/topics/HealthProbes) — the health half of the management surface,
and [REST Server — Mixins and Multi-Mount
Paths](/docs/topics/RestServerCompositionMixinsAndPaths) — the
`@Rest(mixins=...)` composition primitives this page builds on.
+
+Starting with **10.0.0**, Juneau ships a coherent, discoverable
**Actuator-style management surface** while preserving Juneau's a-la-carte
composability. Every endpoint exists in two independently-mountable flavors — a
composable `*Mixin` and a routed `*Resource` — sharing one worker, and a
convenience `BasicActuatorGroup` assembles them under a single configurable
path prefix.
+
+## Endpoint catalog
+
+| Endpoint | What it does | Module | Default exposure |
+| --- | --- | --- | --- |
+| `GET /info` | Application manifest / build / version / git metadata |
`juneau-microservice` | **on** |
+| `GET /loggers`, `GET /loggers/{name}` | Read `java.util.logging` levels |
`juneau-rest-server` | **on** |
+| `PUT`/`POST /loggers/{name}` | Set a JUL level at runtime |
`juneau-rest-server` | **deny-by-default** |
+| `GET /metrics` | Prometheus scrape from a Micrometer registry |
`juneau-rest-server-metrics-micrometer` | on (501 if no registry) |
+| `GET /healthz`, `/readyz`, `/livez` | Health / readiness / liveness probes |
`juneau-rest-server` | **on** |
+| `GET /threaddump` | `ThreadMXBean` thread dump | `juneau-rest-server` |
**deny-by-default** |
+| `GET /heapdump` | HotSpot `.hprof` heap dump | `juneau-rest-server` |
**deny-by-default** |
+
+Endpoints are split across modules by dependency: the pure-JUL `/loggers` and
the JDK-only `/threaddump`/`/heapdump` need only the REST server; `/metrics`
needs Micrometer; `/info` needs the microservice runtime that resolves the
application `ManifestFile`.
+
+## `BasicActuatorGroup` — one-liner assembly
+
+```java
+import org.apache.juneau.microservice.management.*;
+import org.apache.juneau.rest.server.*;
+
+// Mount the whole surface under /actuator.
+@Rest(children=BasicActuatorGroup.class)
+public class RootResources extends BasicRestServletGroup { /* ... */ }
+```
+
+`BasicActuatorGroup` composes `InfoMixin`, `LoggersMixin`, `HealthMixin`, and
`DumpsMixin` under a single prefix. The prefix defaults to `/actuator` and is
configurable via the `juneau.actuator.path` system property (resolved through
the standard `$S{...}` SVL var):
+
+```bash
+java -Djuneau.actuator.path=/manage -jar myapp.jar
+```
+
+> **`/metrics` is not assembled into the group** — it lives in the
`juneau-rest-server-metrics-micrometer` module (which `juneau-microservice`
does not depend on). Add `MetricsMixin` a-la-carte to your host resource when
that module is on the classpath.
+
+Composition is convenience, not the only way in — every endpoint remains
independently mountable a-la-carte via its standalone `*Mixin`/`*Resource`
flavor.
+
+## Exposure / security policy
+
+The surface is **on-by-default-with-gating**: non-sensitive reads are exposed;
mutating/sensitive operations are **deny-by-default** and require an explicit
opt-in bean. No auth provider is auto-wired (explicit-over-magic) — wire your
own guard (e.g. a `BearerTokenGuard`) on top.
+
+There are two complementary layers:
+
+- **Functional gate (deny-by-default):** a settings bean (`LoggersSettings`,
`DumpsSettings`) controls whether the sensitive capability is *enabled at all*.
Off until you opt in.
+- **Authz gate (who may call it):** apply a guard to the sensitive mixin at
composition time with `@Rest(mixinDefs=@Mixin(type=..., guards=...))` — the
host-side override from [Mixin
Sub-Contexts](/docs/topics/RestServerMixinSubContexts#host-side-overrides-with-mixin-1000).
For example, to require `AdminGuard` on the diagnostic dumps:
+
+```java
+import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.management.*;
+
+@Rest(mixinDefs=@Mixin(type=DumpsMixin.class, guards=AdminGuard.class))
+public class MyManagedResource extends BasicRestServlet { ... }
+```
+
+This guards the dump endpoints without subclassing `DumpsMixin` or editing it.
Combine the two layers: `DumpsSettings` enables the dumps, `@Mixin(guards=...)`
restricts who can call them.
+
+### `/loggers` set-level (deny-by-default)
+
+The read endpoints are always on; the `PUT`/`POST` set-level endpoints respond
**403** unless a `LoggersSettings` bean enables writes:
+
+```java
+import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.rest.server.management.*;
+
+@Bean
+LoggersSettings loggersSettings() {
+ return LoggersSettings.create().enableWrite().build();
+}
+```
+
+### `/threaddump` + `/heapdump` (deny-by-default)
+
+Both diagnostics respond **403** unless a `DumpsSettings` bean opts them in
(independently). `/heapdump` additionally responds **501** on a JVM without
HotSpot heap-dump support, and streams a self-deleting `.hprof` temp file when
enabled:
+
+```java
+@Bean
+DumpsSettings dumpsSettings() {
+ return DumpsSettings.create()
+ .enableThreadDump()
+ .enableHeapDump()
+ .build();
+}
+```
+
+> **Why deny-by-default?** A thread dump can leak sensitive runtime state; a
heap dump can be large and contain secrets. Both stay off until an operator
explicitly opts in.
+
+## `/info` and manifest stamping
+
+`/info` renders the running application's `META-INF/MANIFEST.MF` main
attributes — sourced from `Microservice.getManifest()` — as a JSON map. With no
manifest registered it degrades cleanly to `{}`.
+
+To surface **build/version/git** metadata, stamp them into your **application
jar's** manifest at build time. `/info` reads your app jar's manifest, so the
stamping goes in **your** build, not Juneau's. A typical Maven recipe:
+
+```xml
+<!-- 1. Capture git metadata into Maven properties. -->
+<plugin>
+ <groupId>io.github.git-commit-id</groupId>
+ <artifactId>git-commit-id-maven-plugin</artifactId>
+ <executions>
+ <execution><goals><goal>revision</goal></goals></execution>
+ </executions>
+ <configuration>
+ <generateGitPropertiesFile>false</generateGitPropertiesFile>
+ </configuration>
+</plugin>
+
+<!-- 2. Stamp them (plus version/build-time) into MANIFEST.MF. -->
+<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+ </manifest>
+ <manifestEntries>
+ <Build-Time>${maven.build.timestamp}</Build-Time>
+ <Git-Commit-Id>${git.commit.id.abbrev}</Git-Commit-Id>
+ <Git-Branch>${git.branch}</Git-Branch>
+ </manifestEntries>
+ </archive>
+ </configuration>
+</plugin>
+```
+
+`addDefaultImplementationEntries` contributes
`Implementation-Title`/`Implementation-Version`; the `manifestEntries` add the
git/build fields. All of them then appear in `/info`:
+
+```json
+{
+ "Implementation-Version": "1.4.2",
+ "Build-Time": "2026-06-17T00:00:00Z",
+ "Git-Commit-Id": "abc1234",
+ "Git-Branch": "main"
+}
+```
+
+## `/metrics` — Micrometer scrape
+
+`/metrics` strictly consumes a **consumer-provided** `MeterRegistry` resolved
from the bean store — it never auto-registers a default (consistent with
`micrometer-core` being a `provided` dependency). It renders a Prometheus text
scrape when a `PrometheusMeterRegistry` is present and degrades to **501 Not
Implemented** otherwise.
+
+```java
+@Bean
+MeterRegistry meterRegistry() {
+ return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
+}
+```
+
+Mount the endpoint by mixing `MetricsMixin` into your host resource (the
`juneau-rest-server-metrics-micrometer` module must be on the classpath).
+
+## `/loggers` is JUL-only in v1
+
+The `/loggers` endpoint reads and sets **`java.util.logging`** levels — the
logging backend Juneau itself configures. Applications that route logging
through SLF4J→Logback or Log4j2 will **not** have their levels changed by this
endpoint in v1. Backend-aware level control (SLF4J/Logback/Log4j2) is tracked
as a separate follow-on.
diff --git a/sidebars.ts b/sidebars.ts
index 35fefb18e3..7c70682de5 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -1444,6 +1444,11 @@ const sidebars: SidebarsConfig = {
id:
'topics/10.10.RestServerStandaloneVsMixin',
label: '10.10. REST
Server — Standalone vs Mixin Resources',
},
+ {
+ type: 'doc',
+ id:
'topics/10.10.05.RestServerChildrenVsMixins',
+ label: '10.10.5. REST
Server — Children vs Mixins',
+ },
{
type: 'doc',
id:
'topics/10.11.RestServerSelfRegistration',
@@ -2089,6 +2094,11 @@ const sidebars: SidebarsConfig = {
id:
'topics/16.11.GracefulShutdown',
label: '16.11. Graceful
Shutdown & Readiness Gating',
},
+ {
+ type: 'doc',
+ id:
'topics/16.12.ManagementSurface',
+ label: '16.12.
Management Surface (Actuator-style endpoints)',
+ },
],
},
{