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 a382d57ab5 Add config profiles and relaxed binding to the config system
a382d57ab5 is described below

commit a382d57ab50568aee7d4c69501d3a19508cace7e
Author: James Bognar <[email protected]>
AuthorDate: Thu Jun 18 13:47:18 2026 -0400

    Add config profiles and relaxed binding to the config system
---
 pages/release-notes/10.0.0.md                      |  22 ++++
 .../05.16.ConfigProfilesAndRelaxedBinding.md       | 111 +++++++++++++++++++++
 sidebars.ts                                        |   5 +
 3 files changed, 138 insertions(+)

diff --git a/pages/release-notes/10.0.0.md b/pages/release-notes/10.0.0.md
index b7041586fd..f9aaa597ea 100644
--- a/pages/release-notes/10.0.0.md
+++ b/pages/release-notes/10.0.0.md
@@ -78,6 +78,28 @@ Key behaviours:
 See the [View-Based Projection](/docs/topics/ViewProjection) topic page for 
full documentation and a
 Jackson migration table.
 
+### juneau-config / juneau-commons
+
+#### Config profiles + relaxed binding (cloud-native environment config)
+
+Juneau 10.0 adds two Spring-Boot-style ergonomics to the config + settings 
system, both additive and off-by-default
+so existing configs are unaffected:
+
+- **Config profiles** — a `<name>-<profile>` overlay (e.g. `my-stage.cfg`, or 
`my-stage.yml` for YAML bases) is merged
+  over the base config when a profile is active. Profile entries win over 
base, base-only keys survive, and for multiple
+  active profiles the **last one wins**. Activated via 
`Config.Builder.profiles(...)` or the `juneau.profiles.active`
+  key (system property / env var / config). Resolution happens at the store 
layer (`ProfileConfigStore` + `ProfileMerge`),
+  so variable resolution, format handling, and **live reload** (a change to 
the base *or* any active profile file
+  re-fires change listeners) all behave exactly as for a single-file config. 
When the Spring Boot bridge is present,
+  `juneau.profiles.active` **piggybacks on `Environment.getActiveProfiles()`** 
for a single source of truth.
+- **Relaxed binding** — a single logical config key resolves from any common 
environment spelling, so k8s-style
+  env vars map cleanly: `my.prop` / `myProp` / `MySection/myKey` are satisfied 
by `MY_PROP` / `MY_SECTION_MY_KEY`. The
+  default `Settings` env source is wrapped with `RelaxedPropertySource` 
(verbatim-first, so exact-match is preserved);
+  it can wrap any other `PropertySource` too.
+
+See the new [Config Profiles & Relaxed 
Binding](/docs/topics/ConfigProfilesAndRelaxedBinding) topic page for the
+activation conventions, overlay/merge precedence, and the candidate-generation 
rules.
+
 ### juneau-microservice-jetty
 
 #### `JettyMicroservice` zero-config facade + bundled defaults
diff --git a/pages/topics/05.16.ConfigProfilesAndRelaxedBinding.md 
b/pages/topics/05.16.ConfigProfilesAndRelaxedBinding.md
new file mode 100644
index 0000000000..6d07ed6b57
--- /dev/null
+++ b/pages/topics/05.16.ConfigProfilesAndRelaxedBinding.md
@@ -0,0 +1,111 @@
+---
+title: "Config Profiles & Relaxed Binding"
+slug: ConfigProfilesAndRelaxedBinding
+---
+
+Starting in **10.0.0**, Juneau's config system supports two cloud-native 
ergonomics borrowed from the Spring Boot
+world: **environment profiles** (`<name>-<profile>` config overlays) and 
**relaxed binding** (one logical key
+resolvable from any common environment-variable spelling). Both are 
opt-in-friendly and change no existing behavior
+by default.
+
+## Config profiles
+
+A *profile* layers an environment-specific overlay (`dev` / `stage` / `prod`, 
etc.) on top of a base config. Given a
+base config `my.cfg` and an active profile `stage`, the overlay file 
`my-stage.cfg` is merged over the base:
+
+- **profile wins over base** for any section/key the profile redefines;
+- **base-only keys survive** unchanged;
+- when **multiple profiles are active**, the **last one wins** for shared keys 
(earlier profiles still contribute
+  their unique keys).
+
+YAML bases work identically with `my-stage.yml` overlays.
+
+### Activation
+
+Set the active profiles with the `juneau.profiles.active` key — a 
comma-separated list — via a system property, an
+environment variable, or a config key:
+
+```bash
+java -Djuneau.profiles.active=stage,cloud -jar myapp.jar
+# or
+export JUNEAU_PROFILES_ACTIVE=stage,cloud   # relaxed binding maps this to 
juneau.profiles.active
+```
+
+Or programmatically on the config builder:
+
+```java
+var config = Config.create()
+    .name("my.cfg")
+    .profiles("stage", "cloud")   // base my.cfg + my-stage.cfg + my-cloud.cfg 
(cloud wins)
+    .build();
+```
+
+With no active profiles, the base config is used verbatim — existing 
single-file configs are completely unaffected.
+
+### How the overlay works
+
+Profile resolution happens at the **store layer**: when profiles are active, 
the configured `ConfigStore` is wrapped
+so that a read of the base name returns the base contents with each active 
profile's overlay merged on top
+(`ProfileConfigStore` + the `ProfileMerge` engine). Because the merged result 
flows through the normal config parse,
+**variable resolution, format handling, and live reload all behave exactly as 
for a single-file config**.
+
+### Live reload
+
+Profile overlays participate in live reload: a change to the base file **or** 
to any active profile overlay file
+re-runs the merge and fires the usual `ConfigEventListener` change events, so 
a running application picks up
+profile-file edits without a restart.
+
+### Spring Boot piggyback
+
+When the Spring Boot bridge (`juneau-rest-server-springboot`) is present, 
`juneau.profiles.active` **piggybacks on
+Spring's active profiles** — `Environment.getActiveProfiles()` becomes the 
source of truth, so `spring.profiles.active`
+(however it was set: property, env, `SpringApplication.setAdditionalProfiles`, 
…) drives Juneau config-profile
+selection with no separate configuration. An explicit `juneau.profiles.active` 
property still wins if you set one.
+
+## Relaxed binding
+
+Relaxed binding lets a single logical configuration key be supplied in any of 
the common environment conventions and
+still resolve. This is what makes Kubernetes-style environment variables map 
cleanly onto config keys:
+
+| Logical key | Also resolvable from |
+|---|---|
+| `my.prop` | `MY_PROP` |
+| `myProp` | `MY_PROP`, `my.prop` |
+| `MY_PROP` | `my.prop` |
+| `MySection/myKey` | `MY_SECTION_MY_KEY` |
+| `my-prop` | `MY_PROP`, `my.prop` |
+
+By default the system-environment source in the `Settings` singleton is 
wrapped with relaxed binding
+(`Settings.RELAXED_SYSTEM_ENV_SOURCE`), so this works out of the box for 
env-var lookups.
+
+### How candidates are generated
+
+A lookup is always tried **verbatim first**, so exact-match behavior is 
preserved and an already-canonical key resolves
+with no transformation. Only on a miss are relaxed variants tried, in order:
+
+1. **verbatim** — the requested name, unchanged;
+2. **UPPER_UNDERSCORE** — every run of non-alphanumeric characters, and every 
lower→upper camel-case boundary, becomes
+   a single `_`, then the whole name is upper-cased (the dominant env-var 
form);
+3. **lower.dotted** — the same separator collapse joined with `.` and 
lower-cased.
+
+The decorator works in the **lookup-key → candidate-name** direction only (the 
well-defined direction): you ask for a
+known logical key and it generates the environment-convention spellings to 
probe. It never tries to reverse a flat
+environment name (where the section/key boundary is ambiguous) back into a 
`section/key` pair.
+
+### Applying it elsewhere
+
+To add relaxed binding over any other `PropertySource`, wrap it:
+
+```java
+import org.apache.juneau.commons.settings.*;
+
+var settings = Settings.create()
+    .addSource(new RelaxedPropertySource(mySource))
+    .build();
+```
+
+## See also
+
+- [Config Files](/docs/topics/ConfigFiles)
+- [Property Sources](/docs/topics/PropertySources)
+- [juneau-commons Settings](/docs/topics/JuneauCommonsSettings)
diff --git a/sidebars.ts b/sidebars.ts
index 7c70682de5..18eec85119 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -1099,6 +1099,11 @@ const sidebars: SidebarsConfig = {
                                                        id: 
'topics/05.15.YamlConfigFiles',
                                                        label: '5.15. YAML 
Config Files',
                                                },
+                                               {
+                                                       type: 'doc',
+                                                       id: 
'topics/05.16.ConfigProfilesAndRelaxedBinding',
+                                                       label: '5.16. Config 
Profiles & Relaxed Binding',
+                                               },
                                        ],
                                },
                                        {

Reply via email to