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 8048837b47 docs: document per-RestContext @Rest(config=...) @Value 
resolution and release-notes entry (TODO-95)
8048837b47 is described below

commit 8048837b4744ad275db19f2b97abcdd2eb5d89e7
Author: James Bognar <[email protected]>
AuthorDate: Wed May 27 09:04:07 2026 -0400

    docs: document per-RestContext @Rest(config=...) @Value resolution and 
release-notes entry (TODO-95)
    
    Co-authored-by: Cursor <[email protected]>
---
 pages/release-notes/9.5.0.md                   |  36 ++++++--
 pages/topics/02.21.06.ValueAnnotationBasics.md | 118 +++++++++++++++++++++++--
 2 files changed, 140 insertions(+), 14 deletions(-)

diff --git a/pages/release-notes/9.5.0.md b/pages/release-notes/9.5.0.md
index b70d27d7b7..42311de31e 100644
--- a/pages/release-notes/9.5.0.md
+++ b/pages/release-notes/9.5.0.md
@@ -2253,15 +2253,33 @@ see [@Value Framework-Internal 
Adoption](../topics/ValueFrameworkInternal.md) fo
 mapping. The Javadoc cross-references that used to read `{@link 
CallLogger#SP_xxx "..."}`
 have all been rewritten to plain `<js>"..."</js>` literal text.
 
-#### `RestContext` wires its `Config` into `Settings` (TODO-79)
-
-- `RestContext`'s constructor now installs a `ConfigPropertySource` for the 
resource-scoped `Config`
-  onto the process-wide `Settings.get()` source stack, and `destroy()` removes 
it. This lets
-  `@Value("${section/key}")` injections on resource-scoped beans see the same 
`Config` that
-  `$C{section/key}` resolves against — without any further wiring.
-- Because `Settings` walks sources in reverse insertion order, the 
per-resource `Config` wins over
-  the classpath-default `juneau.cfg` bridge installed by 
`ConfigPropertySourceProvider` and over
-  the JVM-default sources (system properties, env vars).
+#### Per-`RestContext` `@Value` resolution against `@Rest(config=...)` Configs 
(TODO-95)
+
+- `RestContext`'s constructor now installs a `PropertySource` named 
`"rest.config"` on the
+  resource's `BeanStore` &mdash; **not** on the process-wide `Settings.get()` 
singleton. The source
+  wraps the resource's `@Rest(config=...)` `Config`(s) and walks them in 
child-to-parent order so
+  the child's keys win on collision and the parent's keys fill the gaps.
+- `@Value("${section/key}")` injection sites on the resource bean (and on 
request-scoped beans
+  whose `BeanStore` parent-walks to the resource's) consult the scoped 
`PropertySource` between
+  the `Settings` local/global override stores and the global `Settings` 
sources chain. Resolution
+  order for these sites is now:
+  1. `Settings.setLocal(...)` / `Settings.setGlobal(...)` overrides (still win 
&mdash; test
+     override semantics are preserved).
+  2. The resource's `@Rest(config=...)` `Config`(s).
+  3. The global `Settings` sources list (system properties, env vars, classpath
+     `ConfigPropertySourceProvider`, microservice `Config`, Spring 
`Environment`, &hellip;).
+  4. The `@Value("${key:default}")` literal default branch.
+- The `BeanStore`-scoped registration means `MockRestClient`'s static 
`RestContext` cache (and
+  any other long-lived cache) cannot leak `ConfigPropertySource`s into the 
global `Settings`
+  singleton &mdash; the 6.7&times; full-suite regression seen during 
FINISHED-79's prototype is
+  structurally impossible. A new `Settings_NoLeakedSources_Test` (200&times; 
`MockRestClient.build`
+  cycle) asserts the source count stays flat across the suite.
+- `@Value Supplier<String>` field types compose with the scoped chain: the 
`BeanStore` reference
+  is captured (by-reference, not snapshotted) by the supplier so every 
`.get()` re-evaluates
+  against the current state of the resource's `PropertySource`s. Hot-reload of 
the underlying
+  `Config` (or `Settings.set(...)` of an override key) takes effect on the 
next `.get()` without
+  rebuilding the resource bean.
+- Cross-link: [@Value Annotation Basics &mdash; Per-resource 
`@Rest(config=...)` 
integration](../topics/ValueAnnotationBasics.md#per-resource-restconfig-integration).
 
 #### Rest Debug Rethink (TODO-20)
 
diff --git a/pages/topics/02.21.06.ValueAnnotationBasics.md 
b/pages/topics/02.21.06.ValueAnnotationBasics.md
index 1420e088b8..f47c870cdc 100644
--- a/pages/topics/02.21.06.ValueAnnotationBasics.md
+++ b/pages/topics/02.21.06.ValueAnnotationBasics.md
@@ -38,7 +38,8 @@ sources) works identically with `${...}`. See
 
 ## Resolution order (precedence)
 
-`Settings.get(name)` walks every registered `PropertySource` in **reverse 
insertion order** — the
+For most `@Value` sites, resolution goes through the process-wide 
`Settings.get(name)` chain:
+`Settings` walks every registered `PropertySource` in **reverse insertion 
order** — the
 most-recently-added source wins. The default chain (built up at startup) looks 
like:
 
 1. **`PropertySourceProvider` SPI sources** added at process startup (e.g. 
classpath `juneau.cfg`
@@ -46,14 +47,17 @@ most-recently-added source wins. The default chain (built 
up at startup) looks l
    the priority list.
 2. **Per-microservice `Config`** — 
`org.apache.juneau.microservice.Microservice` installs a
    `ConfigPropertySource` for its own `Config` in its constructor.
-3. **Per-`RestContext` `Config`** — `RestContext` installs one for the 
`@Rest`-resource-scoped
-   `Config` in its constructor.
-4. **Spring `Environment` bridge** — when `juneau-rest-server-springboot`'s 
`SpringBeanStore` is
+3. **Spring `Environment` bridge** — when `juneau-rest-server-springboot`'s 
`SpringBeanStore` is
    constructed, it installs a `SpringEnvironmentPropertySource` so 
`${spring.app.key}` resolves
    against `application.yaml`, env vars, command-line args, and any custom 
Spring `PropertySource`.
-5. **Built-in `Settings` sources** — system properties, environment variables, 
`.env` file,
+4. **Built-in `Settings` sources** — system properties, environment variables, 
`.env` file,
    argument list, JAR manifest entries.
 
+When the `@Value` site is on a bean that the REST runtime instantiates 
(resource bean fields,
+constructor parameters, request-scoped bean fields), the resolution order 
grows one tier &mdash;
+the resource's [`@Rest(config=...)`](#per-resource-restconfig-integration) 
`Config` participates
+between the `Settings` override layer and the chain above.
+
 Because all sources are checked, the same `@Value` expression resolves 
identically under a
 microservice, a Spring Boot app, or a bare REST resource — the *origin* of the 
value changes, the
 expression doesn't.
@@ -211,6 +215,110 @@ public class MyResource extends BasicSpringRestServlet {
 The bridge is removed when `SpringBeanStore.clear()` is called — important for 
tests that build
 many transient Spring contexts in the same JVM.
 
+## Per-resource `@Rest(config=...)` integration 
{#per-resource-restconfig-integration}
+
+When a REST resource declares `@Rest(config="my.cfg")`, the resolved `Config` 
is wired into the
+resource's `BeanStore` as a <a 
href="/site/apidocs/org/apache/juneau/commons/settings/PropertySource.html" 
target="_blank">PropertySource</a>
+bean named `"rest.config"`. Any `@Value("${some-key}")` site on the resource 
bean (or on a
+request-scoped bean whose `BeanStore` parent-walks to the resource's) consults 
this scoped
+`PropertySource` between the `Settings` local/global overrides and the global 
`Settings` sources
+chain.
+
+### Resolution order for resource-scoped `@Value` sites
+
+1. **`Settings.setLocal(...)` / `Settings.setGlobal(...)` overrides** &mdash; 
still win. The test
+   override contract is preserved (overrides beat resource Configs).
+2. **The resource's `@Rest(config=...)` `Config`(s)** &mdash; via the 
per-`BeanStore`
+   `PropertySource` bean.
+3. **The process-wide `Settings.get(name)` chain** &mdash; reverse-walks the 
registered sources
+   from the [resolution-order section](#resolution-order-precedence) above.
+4. **The `@Value` default branch** &mdash; the `${key:default}` literal.
+
+### Concrete example
+
+`api.cfg` on the classpath / cwd:
+
+```ini
+api.key = secret-A
+api.url = https://a.example.org/
+
+[section]
+nested = nested-value
+```
+
+```java
+@Rest(config="api.cfg")
+public class MyResource extends BasicRestServlet {
+
+    @Value("${api.key}")              // → "secret-A"
+    String apiKey;
+
+    @Value("${section/nested}")       // → "nested-value"
+    String nested;
+
+    @Value("${unknown:fallback}")     // → "fallback" (key absent, default 
used)
+    String fallback;
+
+    @Value("${java.home}")            // → System property (falls through to 
Settings)
+    String javaHome;
+}
+```
+
+### Isolation guarantee
+
+Each `RestContext` carries its own `BeanStore` with its own `"rest.config"` 
bean. Resource A's
+`@Value("${foo}")` resolves against `A.cfg`, Resource B's `@Value("${foo}")` 
resolves against
+`B.cfg` &mdash; the two never see each other's `Config`. The same isolation 
extends to
+`MockRestClient`'s static `RestContext` cache and to long-lived Spring Boot 
contexts because
+the registration lives on the per-resource `BeanStore`, not on the 
process-wide singleton.
+
+### Inheritance (child wins, parent fills gaps)
+
+When `@Rest(config="parent.cfg")` on `ParentResource` and 
`@Rest(config="child.cfg")` on
+`ChildResource extends ParentResource` declare different config names, the 
child's keys win on
+collision and the parent's keys fill the gaps:
+
+```ini
+# parent.cfg
+parent.only = parent-value
+shared      = parent-shared
+```
+
+```ini
+# child.cfg
+child.only  = child-value
+shared      = child-shared
+```
+
+```java
+@Rest(config="parent.cfg")
+public class ParentResource extends BasicRestServlet {
+    @Value("${parent.only}")  String parentOnly;
+    @Value("${shared}")       String shared;
+}
+
+@Rest(config="child.cfg")
+public class ChildResource extends ParentResource {}
+```
+
+A `ChildResource` instance sees `child-value` for `child.only`, `parent-value` 
for `parent.only`
+(child's `Config` doesn't define it &mdash; parent fills the gap), and 
`child-shared` for
+`shared` (child wins on collision).
+
+### `setLocal` / `setGlobal` still win
+
+The resource-scoped `Config` is consulted *after* the `Settings` override 
stores, so existing
+test fixtures that call `Settings.get().setGlobal("api.key", "from-test")` 
continue to take
+precedence over `api.cfg` &mdash; matching the established override contract.
+
+### `Supplier<String>` re-evaluates against the scoped `BeanStore`
+
+`@Value Supplier<String>` field types compose with the resource-scoped chain: 
the `BeanStore`
+reference is captured (by-reference, not snapshotted) by the supplier, so 
every `.get()` call
+re-evaluates against the current state of the resource's `PropertySource`s. 
This means hot-
+reload of the resource Config (or `Settings.set(...)` of an override key) 
takes effect on the
+next `.get()` call without rebuilding the resource bean.
+
 ## Classpath-default `Config` (`juneau-config`)
 
 The `juneau-config` module ships a `META-INF/services` entry for

Reply via email to