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 2deecc264e SVL overhaul docs: #{...} scripting + VarTemplate + @Value 
Supplier<String>
2deecc264e is described below

commit 2deecc264e857dae54857509ebbd8c51b7d3ec51
Author: James Bognar <[email protected]>
AuthorDate: Tue May 26 08:33:53 2026 -0400

    SVL overhaul docs: #{...} scripting + VarTemplate + @Value Supplier<String>
    
    Companion to the juneau-side SVL overhaul commit:
    - Release-notes section in 9.5.0 covering the #{...} scripting syntax,
      the ~68-function built-in catalog, hard-break removal of 11 transformation
      Var classes, the new VarTemplate compilation API, @Value Supplier<String>
      field-type support, and stable-value folding.
    - Topic-page rewrite for SimpleVariableLanguageBasics covering #{...}
      syntax, VarFunction SPI, type coercion, VarTemplate, and the
      Patterns-emerging-from-composition showcase.
    - ValueAnnotationBasics extended with Supplier<String> re-evaluating-reads
      section.
    - SvlVariables trimmed to remove rows for the 11 retired Vars + migration
      note pointing at the #{...} replacements.
    
    Co-authored-by: Cursor <[email protected]>
---
 pages/release-notes/9.5.0.md                       | 159 +++++++++++++++
 .../02.21.01.SimpleVariableLanguageBasics.md       | 224 +++++++++++++++++++--
 pages/topics/02.21.06.ValueAnnotationBasics.md     |  64 +++++-
 pages/topics/10.13.SvlVariables.md                 |  13 +-
 4 files changed, 435 insertions(+), 25 deletions(-)

diff --git a/pages/release-notes/9.5.0.md b/pages/release-notes/9.5.0.md
index 5a9b3663c3..551adbb311 100644
--- a/pages/release-notes/9.5.0.md
+++ b/pages/release-notes/9.5.0.md
@@ -411,6 +411,165 @@ land, parser sessions other than `JsonParserSession` / 
`Json5ParserSession` prod
 - `Optional<T>` field/parameter injection collapses `null` and empty-string 
results to
   `Optional.empty()` — matching the "missing-key" intuition that consumers 
expect.
 
+#### SVL overhaul: `#{...}` scripting + `VarTemplate` compilation (TODO-102 + 
TODO-103)
+
+This is a **major-release breaking change** in the SVL surface. The 11 
single-purpose
+transformation `Var` classes are removed without `@Deprecated` shims; their 
behaviour is
+recovered through the new `#{name(args)}` function-call syntax.
+
+##### New `#{...}` function-call syntax
+
+- Introduced the `#{name(arg1, arg2, ...)}` script-call form inside any SVL 
string.
+  Tokenized by the same recursive-descent compiler that handles `${...}` and 
`$X{...}`, so the
+  three forms nest naturally (`#{upper(${user.name})}`, 
`${prefix:#{lower(${value})}}`, …).
+- Added the `org.apache.juneau.commons.svl.VarFunction` SPI plus 
`TypedFunction` reflection
+  helper. `TypedFunction` derives arity and per-arg types from the declared 
`invoke(...)` method
+  signature(s) and routes string args through `ArgCoercer` for coercion to 
`int` / `long` /
+  `double` / `boolean` / `String` / `String[]` / `Object`.
+- Boolean truthiness is centralised in `ArgCoercer`: `"true"`, `"yes"`, 
`"on"`, `"1"` are truthy;
+  everything else (including `null` and empty string) is falsy. `#{if(...)}`, 
`#{not(...)}`,
+  `#{switch(...)}`, etc. all share the same rule.
+- `#{switch(...)}` matches each case pattern as a glob (`*` = any sequence, 
`?` = single char),
+  preserving the legacy `$SW{...}` Var's matching semantics.
+- `#{len(s, delimiter)}` adds a two-argument overload that counts parts after 
split-by-delimiter,
+  preserving the legacy `$LN{s, delim}` Var's two-arg behaviour. The 
single-arg form still
+  returns character length.
+
+##### Built-in function catalog (~68 functions, 10 categories)
+
+| Category | Examples |
+|---|---|
+| String | `upper`, `lower`, `len`, `substring`, `trim`, `split`, `join`, 
`format`, `concat`, `pathToken` |
+| Type conversion | `toInt`, `toLong`, `toDouble`, `toBoolean`, `toString` |
+| Arithmetic | `add`, `sub`, `mul`, `div`, `mod`, `min`, `max`, `abs`, 
`round`, `floor`, `ceil` |
+| Boolean | `not`, `and`, `or`, `xor`, `eq`, `ne`, `lt`, `gt`, `le`, `ge` |
+| Conditional | `if`, `switch`, `coalesce`, `notEmpty`, `case` |
+| Regex | `matches`, `replace`, `extract` |
+| Encoding | `urlEncode`, `urlDecode`, `base64Encode`, `base64Decode`, 
`htmlEscape`, `htmlUnescape` |
+| Date/time | `now`, `today`, `formatDate`, `parseDate` |
+| Random/UUID | `uuid`, `randomInt`, `randomLong`, `randomDouble`, 
`randomString`, `randomChar` |
+| JSON navigation | `jsonPath`, `get`, `keys`, `values`, `size` |
+
+See <a 
href="/site/apidocs/org/apache/juneau/commons/svl/functions/package-summary.html"
 target="_blank">org.apache.juneau.commons.svl.functions</a>
+for the per-class function lists with full signatures and arg style notes.
+
+##### Function discovery
+
+Three discovery channels, evaluated in order:
+
+1. **Explicit registration** — 
`VarResolver.create().functions(MyFn.class).build()`.
+2. **`ServiceLoader` discovery** — declare your implementation in
+   `META-INF/services/org.apache.juneau.commons.svl.VarFunction`. 
`VarResolver.create().defaultFunctions().build()`
+   and `VarResolver.DEFAULT` both load these automatically.
+3. **`BeanStore` lookup** — built-in functions are constructed through the
+   `VarResolver.Builder`'s `BeanStore` so they can themselves take constructor 
`@Inject`/`@Value`
+   dependencies (rarely needed but available).
+
+Unknown function names fail lazily at resolve time (`No such function 'foo'`) 
rather than at
+compile time, matching the existing `Var` registry behaviour.
+
+##### Hard-break removal of 11 `Var` classes
+
+The following 11 transformation `Var` classes are **removed without 
`@Deprecated` shims**:
+
+| Removed `Var` | Removed prefix | Replacement |
+|---|---|---|
+| `IfVar` | `$IF{...}` | `#{if(cond, then, else)}` |
+| `SwitchVar` | `$SW{...}` | `#{switch(value, pat1:result1, ..., *:default)}` 
(glob semantics preserved) |
+| `CoalesceVar` | `$CO{...}` | `#{coalesce(a, b, c, ...)}` |
+| `NotEmptyVar` | `$NE{...}` | `#{notEmpty(arg)}` |
+| `PatternMatchVar` | `$PM{...}` | `#{matches(value, pattern)}` |
+| `PatternReplaceVar` | `$PR{...}` | `#{replace(value, pattern, replacement)}` 
|
+| `PatternExtractVar` | `$PE{...}` | `#{extract(value, pattern, groupIndex)}` |
+| `UpperCaseVar` | `$UC{...}` | `#{upper(arg)}` |
+| `LowerCaseVar` | `$LC{...}` | `#{lower(arg)}` |
+| `LenVar` | `$LN{...}` | `#{len(arg)}` (single-arg) or `#{len(arg, 
delimiter)}` (two-arg parity) |
+| `SubstringVar` | `$ST{...}` | `#{substring(arg, start)}` or 
`#{substring(arg, start, end)}` |
+
+`VarList.addDefault()` no longer registers the removed Vars; the
+`org.apache.juneau.commons.svl.vars` package no longer ships their classes. 
Any user code or
+template that still references the removed prefixes fails at resolve time with
+`No such var '<prefix>'`.
+
+The source-data Vars (`$E{}`, `$S{}`, `$MF{}`, `$A{}`, `$P{}` and the shortcut 
`${}`) are
+unchanged.
+
+##### New `VarTemplate` compiled-template API (TODO-103)
+
+- Added `VarResolver.compile(String)` → `VarTemplate` — tokenizes a template 
once and returns a
+  reusable, threadsafe compiled form. Subsequent `template.resolve(session)` 
calls skip
+  tokenization + var-registry lookup, walking the cached segment array 
directly.
+- Added `VarResolver.resolveSupplier(String)` → `Supplier<String>` — opens a 
fresh session on
+  every `.get()` call. Safe to share across threads. Recommended default.
+- Added `VarResolverSession.compile(String)` and 
`VarResolverSession.resolveSupplier(String)` —
+  symmetric session-bound variants. The session-bound Supplier inherits the 
session's
+  threadsafety contract (i.e. not threadsafe by default).
+- Added `VarTemplate.isLiteral()` — `true` when the template resolves to a 
fixed string (empty
+  template, no variables, or all variables fold to literals via stable-value 
folding). Callers
+  can fast-path purely-literal inputs.
+- Refactored `VarResolverSession.resolve(String)` to delegate to
+  `compile(input).resolve(session)` so there is a single tokenizer + 
segment-resolution path in
+  the codebase. No behaviour change for existing callers.
+
+##### `@Value Supplier<String>` field-type autodetect
+
+- `BeanInstantiator` now inspects the declared field/parameter type at 
injection sites carrying
+  `@Value`. A `Supplier<String>` field receives a re-evaluating, threadsafe 
Supplier (delegates
+  to `VarTemplate.asSupplierWithFreshSessions(...)`); a bare `String` field 
gets the one-shot
+  resolved value as before. **No `@Value(supplier=true)` flag is required — 
the field type IS
+  the opt-in signal.**
+- Literal-only expressions (`@Value("plain") Supplier<String>`) take a 
constant-folding fast
+  path that returns the same captured `String` reference on every `.get()` 
call, avoiding the
+  per-call session allocation entirely.
+- Worked patterns for live UUID factories, idempotency keys, jittered delays, 
and hot-reload
+  config knobs are in the [`@Value` topic 
page](/docs/topics/ValueAnnotationBasics#supplierstring-field-type--re-evaluating-reads-950).
+
+##### Compile-time stable-value folding
+
+- New `default boolean Var.isStable() { return false; }` SPI hook. Opt-in per 
`Var`
+  implementation; conservative `false` default protects third-party `Var`s 
from accidental
+  incorrect folding.
+- Four built-in `Var`s opt in: `EnvVariablesVar`, `SystemPropertiesVar`, 
`ManifestFileVar`,
+  `ArgsVar`. Compiled templates eagerly resolve these at compile time and 
replace the segment
+  with a `LiteralSegment` — runtime dispatch is eliminated.
+- `SystemPropertiesVar`'s opt-in carries a documented caveat: 
`System.setProperty(...)` calls
+  *after* compile do not propagate to a folded template. In practice this is 
the desired
+  behaviour for the common "read once at startup" use case.
+- `ConfigVar`, `PropertyVar`, `DotenvVar`, `EnvFileVar` stay non-stable. Their 
backing source
+  can mutate at runtime (config reload, file watch, runtime `Settings.set`), 
so folding would
+  produce stale reads.
+
+##### Internal perf
+
+- `BeanInstantiator`'s `@Value` resolution path caches a `VarTemplate` per 
distinct expression
+  in `ValueResolver.TEMPLATE_CACHE`. Repeated bean construction reads the 
cached form rather
+  than re-tokenizing.
+- `RestOpContext.pathMatchers` Memoizer now goes through 
`vr.compile(p).resolve(session)`
+  explicitly so the framework hot loop exercises the compiled-template seam. 
If we ever switch
+  to per-request dynamic path resolution, only the `.resolve(session)` step 
moves to the
+  request handler — the compile is already done at context-build time.
+- Micro-benchmark (`juneau-utest/.../VarResolver_Benchmark_Test`): 
compile-once + resolve-N is
+  ~1.7× faster than compile-per-call for plain `${...}` templates and ~1.4× for
+  `#{...}` script templates on a representative machine. The headline `≥ 2× / 
≥ 5×` targets
+  from the plan apply to comparisons against the pre-9.5 legacy ad-hoc 
dispatcher, which is no
+  longer in the tree — the unified compiled-form path is now the baseline.
+
+##### Migration
+
+For any SVL template that uses one of the 11 removed `Var` prefixes, rewrite 
to `#{...}`. Cookbook:
+
+| Before | After |
+|---|---|
+| `$UC{$S{user.name}}` | `#{upper(${user.name})}` |
+| `$IF{$S{prod},prod.key,dev.key}` | `#{if(${prod}, prod.key, dev.key)}` |
+| `$SW{$S{os},*win*:Windows,*:Unix}` | `#{switch(${os}, *win*:Windows, 
*:Unix)}` |
+| `$PR{$S{name},[aeiou],*}` | `#{replace(${name}, [aeiou], *)}` |
+| `$LN{a,b,c,,}` | `#{len(a,b,c, ,)}` |
+| `$ST{hello,2,4}` | `#{substring(hello, 2, 4)}` |
+
+The full `${...}` shortcut, `$S{}`/`$E{}`/`$MF{}`/`$A{}`/`$P{}` source vars, 
and `$C{}` config
+var keep their existing syntax — no migration required for those.
+
 ### juneau-config
 
 #### Classpath-default `Config` bridge to `Settings` (TODO-79)
diff --git a/pages/topics/02.21.01.SimpleVariableLanguageBasics.md 
b/pages/topics/02.21.01.SimpleVariableLanguageBasics.md
index 7f240d5816..b53365f0c6 100644
--- a/pages/topics/02.21.01.SimpleVariableLanguageBasics.md
+++ b/pages/topics/02.21.01.SimpleVariableLanguageBasics.md
@@ -15,28 +15,21 @@ themselves contain more variables.
 The <a href="/site/apidocs/org/apache/juneau/commons/svl/VarResolver.html" 
target="_blank">VarResolver</a> class (in module `juneau-commons`) is used to 
resolve variables.
 
 The <a 
href="/site/apidocs/org/apache/juneau/commons/svl/VarResolver.html#DEFAULT" 
target="_blank">VarResolver.DEFAULT</a> resolver is a reusable instance
-of this class configured with the following basic variables:
+of this class configured with the following basic source variables:
 
 <tree>
 <node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/SystemPropertiesVar.html"
 target="_blank">SystemPropertiesVar</a></java-class> - 
`$S{key[,default]}`</node-0>
 <node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/EnvVariablesVar.html" 
target="_blank">EnvVariablesVar</a></java-class> - `$E{key[,default]}`</node-0>
+<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/ArgsVar.html" 
target="_blank">ArgsVar</a></java-class> - `$A{key[,default]}`</node-0>
+<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/ManifestFileVar.html" 
target="_blank">ManifestFileVar</a></java-class> - `$MF{key[,default]}`</node-0>
+<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/PropertyVar.html" 
target="_blank">PropertyVar</a></java-class> - `$P{key[,default]}` (the unified 
`Settings` source — see `@Value` topic)</node-0>
 </tree>
 
-The following logic variables are also provided:
-
-<tree>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/IfVar.html" 
target="_blank">IfVar</a></java-class> - `$IF{arg,then[,else]}`</node-0>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/SwitchVar.html" 
target="_blank">SwitchVar</a></java-class> - 
`$SW{arg,pattern1:then1[,pattern2:then2...]}`</node-0>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/CoalesceVar.html" 
target="_blank">CoalesceVar</a></java-class> - `$CO{arg1[,arg2...]}`</node-0>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/PatternMatchVar.html" 
target="_blank">PatternMatchVar</a></java-class> - `$PM{arg,pattern}`</node-0>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/PatternReplaceVar.html" 
target="_blank">PatternReplaceVar</a></java-class> - 
`$PR{arg,pattern,replace}`</node-0>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/PatternExtractVar.html" 
target="_blank">PatternExtractVar</a></java-class> - 
`$PE{arg,pattern,groupIndex}`</node-0>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/NotEmptyVar.html" 
target="_blank">NotEmptyVar</a></java-class> - `$NE{arg}`</node-0>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/UpperCaseVar.html" 
target="_blank">UpperCaseVar</a></java-class> - `$UC{arg}`</node-0>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/LowerCaseVar.html" 
target="_blank">LowerCaseVar</a></java-class> - `$LC{arg}`</node-0>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/LenVar.html" 
target="_blank">LenVar</a></java-class> - `$LN{arg[,delimiter]}`</node-0>
-<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/SubstringVar.html" 
target="_blank">SubstringVar</a></java-class> - `$ST{arg,start[,end]}`</node-0>
-</tree>
+Conditional/transformation logic that used to live in dedicated `Var` classes 
(`$UC{}` upper-case,
+`$IF{}` ternary, `$SW{}` switch, etc.) has moved to the `#{...}` function-call 
syntax — see
+[`#{...}` function-call syntax (9.5.0)](#-function-call-syntax-950) below. The 
full migration table
+of the 11 removed `Var` classes and their `#{...}` replacements is in the 
[9.5.0 release
+notes](../release-notes/9.5.0.md).
 
 :::tip Example
 ```java
@@ -58,7 +51,7 @@ String property = 
VarResolver.DEFAULT.resolve("$E{MYPROPERTY,$S{my.property,not
 
 ## The `${xxx}` shortcut (9.5.0)
 
-Starting in 9.5.0 the tokenizer recognises `${xxx}` as a shorthand for 
`$P{xxx}` — the
+The tokenizer recognises `${xxx}` as a shorthand for `$P{xxx}` — the
 <a href="/site/apidocs/org/apache/juneau/commons/svl/vars/PropertyVar.html" 
target="_blank">PropertyVar</a>
 that resolves against the unified `Settings` source stack (system properties, 
env vars,
 classpath `juneau.cfg`, per-microservice `Config`, Spring `Environment`, …). A 
Spring-style
@@ -73,3 +66,200 @@ VarResolver.DEFAULT.resolve("${my.key:fallback}");
 
 See [@Value Annotation Basics](./ValueAnnotationBasics.md) for the broader 
picture of how
 `${...}` ties into declarative configuration injection and Spring Boot interop.
+
+## `#{...}` function-call syntax (9.5.0)
+
+Alongside the variable forms above, `VarResolver.DEFAULT` now ships a built-in 
function library
+that's invoked via `#{name(args...)}`:
+
+```java
+VarResolver.DEFAULT.resolve("#{upper(hello world)}");                     // → 
"HELLO WORLD"
+VarResolver.DEFAULT.resolve("#{if(${prod:false}, prod-key, dev-key)}");  // → 
"dev-key"
+VarResolver.DEFAULT.resolve("#{switch(${tier:bronze}, gold:1.0, silver:0.5, 
*:0)}"); // → "0"
+VarResolver.DEFAULT.resolve("#{len(${csv}, ,)}");                         // → 
"3" (delimiter-aware part count)
+```
+
+The catalog ships ~68 built-in functions across 10 categories:
+
+- **String** — `upper`, `lower`, `len`, `substring`, `trim`, `split`, `join`, 
`format`, `pathToken`, …
+- **Type conversion** — `toInt`, `toLong`, `toDouble`, `toBoolean`, 
`toString`, …
+- **Arithmetic** — `add`, `sub`, `mul`, `div`, `mod`, `min`, `max`, `abs`, 
`round`, …
+- **Boolean** — `not`, `and`, `or`, `xor`, `eq`, `ne`, …
+- **Conditional** — `if`, `switch`, `coalesce`, `notEmpty`, `case`, …
+- **Regex** — `matches`, `replace`, `extract`, …
+- **Encoding** — `urlEncode`, `urlDecode`, `base64Encode`, `base64Decode`, 
`htmlEscape`, `htmlUnescape`, …
+- **Date/time** — `now`, `today`, `formatDate`, `parseDate`, …
+- **Random/UUID** — `uuid`, `randomInt`, `randomString`, …
+- **JSON navigation** — `jsonPath`, `get`, `keys`, `values`, `size`, …
+
+See <a 
href="/site/apidocs/org/apache/juneau/commons/svl/functions/package-summary.html"
 target="_blank">org.apache.juneau.commons.svl.functions</a>
+for the per-class function lists with full signatures.
+
+### Type coercion
+
+Function arguments are auto-coerced from their resolved string form to the 
function's declared
+parameter type (via `TypedFunction` reflection + `ArgCoercer`). Supported 
coercions:
+
+- Primitive numerics (`int`, `long`, `double`) and their boxed equivalents.
+- `boolean` — truthy values are `"true"`, `"yes"`, `"on"`, `"1"`; everything 
else is `false`
+  (including `null` and empty string). Boolean tests in `#{if(...)}` and 
`#{switch(...)}` follow
+  the same rule.
+- `String[]` — a JSON-array literal (`["a","b","c"]`) or comma-separated 
tokens, depending on the
+  function's documented arg style.
+- `String` — passed through verbatim.
+
+### Glob matching in `switch(...)`
+
+`#{switch(value, pat1:result1, pat2:result2, ..., *:default)}` matches each 
pattern as a glob:
+`*` matches any character sequence, `?` matches a single character. The legacy 
`$SW{...}` Var
+used the same semantics; the function form preserves that exactly.
+
+### `VarFunction` SPI for user-defined functions
+
+User code can publish additional functions through:
+
+1. **Explicit registration** — 
`VarResolver.create().functions(MyFunction.class).build()`.
+2. **`ServiceLoader` discovery** — declare your `VarFunction` class in
+   `META-INF/services/org.apache.juneau.commons.svl.VarFunction`. Picked up 
automatically by
+   `VarResolver.create().defaultFunctions().build()` and by 
`VarResolver.DEFAULT`.
+3. **Per-RestContext** — `@RestInject(name="varResolver")` to install a custom 
resolver, then
+   `.functions(...)` on the builder.
+
+Subclass `TypedFunction` to get reflection-derived arity + arg type coercion 
for free, or
+implement `VarFunction` directly for full control.
+
+## `VarTemplate` compiled-template API (9.5.0)
+
+`VarResolver.compile(input)` returns a precompiled
+<a href="/site/apidocs/org/apache/juneau/commons/svl/VarTemplate.html" 
target="_blank">VarTemplate</a>
+that tokenizes the template once and caches the segment array. Subsequent 
`resolve(...)` calls
+skip tokenization and var-registry lookup, walking the cached segments 
directly.
+
+```java
+// Compile once at startup (cache on field, in framework metadata, etc.).
+VarTemplate t = VarResolver.DEFAULT.compile("hello ${user.name:guest}");
+
+// Resolve as many times as you want.
+String s = t.resolve(VarResolver.DEFAULT.createSession());
+```
+
+The same `VarResolverSession.resolve(String)` you already use is built on top 
of `compile(...)`
+internally — calling `compile(...)` explicitly is an opt-in optimization for 
callers that
+resolve the same template repeatedly (framework injection sites, request 
routers, etc.).
+
+### `resolveSupplier(...)` for live-reload
+
+`vr.resolveSupplier(input)` returns a `Supplier<String>` that re-evaluates the 
template on every
+`.get()` call against a fresh session, so the returned Supplier is safe to 
share across threads:
+
+```java
+Supplier<String> live = 
VarResolver.DEFAULT.resolveSupplier("${myapp.title:Untitled}");
+// Anywhere across the app:
+String now = live.get();
+```
+
+### Which Supplier do I want?
+
+| You want… | Use | Notes |
+|---|---|---|
+| Re-evaluate on every `.get()`, safe across threads. | 
`vr.resolveSupplier(...)` or `tpl.asSupplierWithFreshSessions(vr)` | Opens a 
fresh session per call. **Recommended default.** |
+| Re-evaluate on every `.get()`, but I own the session. | 
`session.resolveSupplier(...)` or `tpl.asSupplier(session)` | Captures the 
existing session — inherits its threadsafety contract. Perf-sensitive callers 
only. |
+| Resolve once at compile/build time. | `tpl.resolve(currentSession)` | 
One-shot. |
+
+### `VarTemplate.isLiteral()`
+
+For templates that contain no variables (or whose variables fold to literals 
via stable-value
+folding — see below), `isLiteral()` returns `true` and `resolve(...)` returns 
a pre-computed
+String constant. Callers can skip Supplier wrapping for literal-only inputs.
+
+### Stable-value folding
+
+Four built-in Vars opt in to compile-time folding via the `Var.isStable()` SPI:
+
+- `EnvVariablesVar` (`$E{...}`) — process env is immutable for the JVM 
lifetime.
+- `SystemPropertiesVar` (`$S{...}`) — folds at compile time; 
`System.setProperty(...)` calls
+  *after* compile do **not** propagate. Documented caveat.
+- `ManifestFileVar` (`$MF{...}`) — classpath manifest entries are immutable.
+- `ArgsVar` (`$A{...}`) — process argv is immutable.
+
+When the compiler sees one of these in a template with a literal body, it 
eagerly resolves the
+value and replaces the segment with a `LiteralSegment`. The runtime dispatch 
is eliminated.
+
+`ConfigVar`, `PropertyVar`, `DotenvVar`, `EnvFileVar` stay non-stable — their 
backing source can
+mutate (config reload, file watch, runtime `Settings.set`), so folding would 
produce stale reads.
+
+## `Supplier<String>` field-type semantics
+
+Within `@Value`-driven configuration injection (see [@Value Annotation
+Basics](./ValueAnnotationBasics.md)), the **field type** controls when the SVL 
expression is
+re-evaluated:
+
+| Declared field type | When does it resolve? | Threadsafe? |
+|---|---|---|
+| `String` | Once at bean construction. The captured value is held for the 
bean's lifetime. | Trivially — it's just a `String`. |
+| `Supplier<String>` | On every `Supplier.get()` call. Each call opens a fresh 
`VarResolverSession`. | Yes. |
+
+No `@Value(supplier=true)` flag is required. The field type *is* the opt-in 
signal.
+
+```java
+public class JwtValidator {
+
+    // Resolves once. If "jwt.cache.ttl" changes after bean creation, this 
stays stale.
+    @Value("${jwt.cache.ttl:PT5M}")
+    Duration ttl;
+
+    // Re-evaluates on every .get(). Pick up live reloads automatically.
+    @Value("${jwt.cache.ttl:PT5M}")
+    Supplier<String> liveTtl;
+}
+```
+
+### Patterns emerging from composition
+
+Combining the `Supplier<String>` field type with the `#{...}` function library 
produces
+expressive live-factory patterns:
+
+```java
+// Request-ID factory — fresh UUID per .get().
+@Value("#{uuid()}")
+Supplier<String> requestId;
+
+// Idempotency key for a downstream API — random per call.
+@Value("#{concat(ord-, #{randomString(12)})}")
+Supplier<String> idempotencyKey;
+
+// Jittered retry delay — random 100–500ms each call.
+@Value("PT0.#{randomInt(100, 500)}S")
+Supplier<String> retryDelayIso;
+
+// Live ISO timestamp.
+@Value("#{now()}")
+Supplier<String> nowIso;
+
+// Hot-reload config knob — re-reads from Settings on every .get(),
+// so an external "Settings.set(...)" call surfaces without re-creating the 
bean.
+@Value("${myapp.cache.policy:write-through}")
+Supplier<String> cachePolicy;
+```
+
+### Stable-folding interaction
+
+Functions (`#{...}`) are **never folded** — `#{uuid()}` inside a 
`Supplier<String>` field always
+produces a fresh value per `.get()`. By contrast, a `${env.HOME}` reference 
inside a
+`Supplier<String>` field is folded at compile time because 
`EnvVariablesVar.isStable()` returns
+`true` — every `.get()` returns the same captured value (no Supplier overhead 
beyond returning the
+literal).
+
+The takeaway: the "fresh per `.get()`" guarantee applies to **functions** and 
**non-stable
+`Var`s**. Stable `Var` lookups behave like resolve-once even inside a 
`Supplier<String>` field.
+This is intentional — those values can't actually change.
+
+## See also
+
+- [@Value Annotation Basics](./ValueAnnotationBasics.md) — declarative config 
injection.
+- [Variable Basics](./VariableBasics.md) — how variables work inside a 
`Config`.
+- [SVL Variables](./SvlVariables.md) — REST-context variable list.
+- <a href="/site/apidocs/org/apache/juneau/commons/svl/VarResolver.html" 
target="_blank">`org.apache.juneau.commons.svl.VarResolver`</a>
+- <a href="/site/apidocs/org/apache/juneau/commons/svl/VarTemplate.html" 
target="_blank">`org.apache.juneau.commons.svl.VarTemplate`</a>
+- <a href="/site/apidocs/org/apache/juneau/commons/svl/VarFunction.html" 
target="_blank">`org.apache.juneau.commons.svl.VarFunction`</a>
+- <a 
href="/site/apidocs/org/apache/juneau/commons/svl/functions/package-summary.html"
 target="_blank">`org.apache.juneau.commons.svl.functions`</a>
diff --git a/pages/topics/02.21.06.ValueAnnotationBasics.md 
b/pages/topics/02.21.06.ValueAnnotationBasics.md
index 2c062d88b5..1420e088b8 100644
--- a/pages/topics/02.21.06.ValueAnnotationBasics.md
+++ b/pages/topics/02.21.06.ValueAnnotationBasics.md
@@ -102,6 +102,66 @@ public class JwtValidator {
 }
 ```
 
+## `Supplier<String>` field type — re-evaluating reads (9.5.0)
+
+Declaring a `@Value` field as `Supplier<String>` instead of `String` makes the 
SVL expression
+re-evaluate on every `.get()` call against a fresh `VarResolverSession`. No 
annotation flag is
+required — **the field type IS the opt-in signal** (per TODO-103 RD #9 
autodetect).
+
+```java
+public class JwtValidator {
+
+    // String field: resolves ONCE at bean construction. Captures whatever 
Settings/PropertyVar
+    // returned at injection time and holds that value for the bean's lifetime.
+    @Value("${jwt.cache.ttl:PT5M}")
+    String ttl;
+
+    // Supplier<String> field: re-evaluates on every .get() call against a 
fresh session.
+    // Picks up live Settings.set(...) updates, file-watch reloads, Spring env 
changes, etc.
+    @Value("${jwt.cache.ttl:PT5M}")
+    Supplier<String> liveTtl;
+}
+```
+
+The returned `Supplier` is **threadsafe** — each `.get()` opens its own 
`VarResolverSession`,
+so it can be cached on shared fields or handed across threads. (Internally 
this delegates to
+`VarTemplate.asSupplierWithFreshSessions(...)`.)
+
+Literal-only expressions (no variables) take a constant-folding fast path: the 
resolved string
+is captured once at compile time and every `.get()` returns the same cached 
value with no
+session overhead.
+
+### Composition with `#{...}` functions
+
+Combining the `Supplier<String>` field type with the `#{...}` function catalog 
produces concise
+live-factory patterns:
+
+```java
+// Request-ID factory — fresh UUID per request.
+@Value("#{uuid()}")
+Supplier<String> requestId;
+
+// Idempotency key for a downstream API — random per call.
+@Value("#{concat(ord-, #{randomString(12)})}")
+Supplier<String> idempotencyKey;
+
+// Jittered retry delay (ISO 8601 duration with 100–500ms jitter).
+@Value("PT0.#{randomInt(100, 500)}S")
+Supplier<String> retryDelayIso;
+
+// Live ISO timestamp on every call.
+@Value("#{now()}")
+Supplier<String> nowIso;
+
+// Hot-reload config — picks up Settings.set(...) without bean re-creation.
+@Value("${myapp.cache.policy:write-through}")
+Supplier<String> cachePolicy;
+```
+
+See [Simple Variable Language Basics — `Supplier<String>` field-type
+semantics](/docs/topics/SimpleVariableLanguageBasics#suppliersstring-field-type-semantics)
 for
+the canonical decision table and stable-folding interaction.
+
 ## Spring `@Value` compatibility
 
 For consumers migrating from Spring Boot, 
`org.springframework.beans.factory.annotation.Value`
@@ -176,10 +236,12 @@ The exception message identifies the offending site to 
make the conflict easy to
 
 ## See also
 
-- [Simple Variable Language Basics](./SimpleVariableLanguageBasics.md) — the 
full SVL surface.
+- [Simple Variable Language Basics](./SimpleVariableLanguageBasics.md) — the 
full SVL surface,
+  the `#{...}` function catalog, and the `VarTemplate` compiled-template API.
 - [SVL Variables](./SvlVariables.md) — list of built-in `$X{...}` resolvers.
 - [Variable Basics](./VariableBasics.md) — how variables work inside a 
`Config`.
 - <a href="/site/apidocs/org/apache/juneau/commons/inject/Value.html" 
target="_blank">org.apache.juneau.commons.inject.Value</a>
 - <a href="/site/apidocs/org/apache/juneau/commons/settings/Settings.html" 
target="_blank">org.apache.juneau.commons.settings.Settings</a>
 - <a href="/site/apidocs/org/apache/juneau/commons/svl/vars/PropertyVar.html" 
target="_blank">org.apache.juneau.commons.svl.vars.PropertyVar</a>
+- <a href="/site/apidocs/org/apache/juneau/commons/svl/VarTemplate.html" 
target="_blank">org.apache.juneau.commons.svl.VarTemplate</a>
 - <a 
href="/site/apidocs/org/apache/juneau/rest/springboot/SpringEnvironmentPropertySource.html"
 
target="_blank">org.apache.juneau.rest.springboot.SpringEnvironmentPropertySource</a>
diff --git a/pages/topics/10.13.SvlVariables.md 
b/pages/topics/10.13.SvlVariables.md
index e50b0ebc67..999cda4dd0 100644
--- a/pages/topics/10.13.SvlVariables.md
+++ b/pages/topics/10.13.SvlVariables.md
@@ -52,13 +52,6 @@ The following is the default list of supported variables:
 | | <a href="/site/apidocs/org/apache/juneau/commons/svl/vars/DotenvVar.html" 
target="_blank">DotenvVar</a> | `$DE{key[,default]}` | yes | yes | 
`$DE{MY_KEY,default}` |
 | | <a href="/site/apidocs/org/apache/juneau/commons/svl/vars/ArgsVar.html" 
target="_blank">ArgsVar</a> | `$A{key[,default]}` | yes | yes | `$A{foo,null}` |
 | | <a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/ManifestFileVar.html" 
target="_blank">ManifestFileVar</a> | `$MF{key[,default]}` | yes | yes | 
`$MF{Main-Class}` |
-| | <a href="/site/apidocs/org/apache/juneau/commons/svl/vars/IfVar.html" 
target="_blank">IfVar</a> | `$IF{arg,then[,else]}` | yes | yes | 
`$IF{$S{my.boolean.property},foo,bar}` |
-| | <a href="/site/apidocs/org/apache/juneau/commons/svl/vars/SwitchVar.html" 
target="_blank">SwitchVar</a> | `$SW{arg,p1:then1[,p2:then2...]}` | yes | yes | 
`$SW{$S{os.name},*win*:Windows,*:Something else}` |
-| | <a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/CoalesceVar.html" 
target="_blank">CoalesceVar</a> | `$CO{arg1[,arg2...]}` | yes | yes | 
`$CO{$S{my.property},$E{my.property},n/a}` |
-| | <a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/PatternMatchVar.html" 
target="_blank">PatternMatchVar</a> | `$PM{arg,pattern}` | yes | yes | 
`$PM{$S{os.name},*win*}` |
-| | <a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/NotEmptyVar.html" 
target="_blank">NotEmptyVar</a> | `$NE{arg}` | yes | yes | `$NE{$S{foo}}` |
-| | <a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/UpperCaseVar.html" 
target="_blank">UpperCaseVar</a> | `$UC{arg}` | yes | yes | `$UC{$S{foo}}` |
-| | <a 
href="/site/apidocs/org/apache/juneau/commons/svl/vars/LowerCaseVar.html" 
target="_blank">LowerCaseVar</a> | `$LC{arg}` | yes | yes | `$LC{$S{foo}}` |
 | **juneau-config** | <a 
href="/site/apidocs/org/apache/juneau/config/vars/ConfigVar.html" 
target="_blank">ConfigVar</a> | `$C{key[,default]}` | yes | yes | 
`$C{REST/staticFiles}` |
 | **juneau-rest-server** | <a 
href="/site/apidocs/org/apache/juneau/rest/vars/FileVar.html" 
target="_blank">FileVar</a> | `$F{path[,default]}` | yes | yes | 
`$F{resources/MyAsideMessage.html, Oops not found!}` |
 | | <a 
href="/site/apidocs/org/apache/juneau/rest/vars/ServletInitParamVar.html" 
target="_blank">ServletInitParamVar</a> | `$I{name[,default]}` | yes | yes | 
`$I{my.param}` |
@@ -75,6 +68,12 @@ The following is the default list of supported variables:
 | | <a href="/site/apidocs/org/apache/juneau/rest/vars/UrlEncodeVar.html" 
target="_blank">UrlEncodeVar</a> | `$UE{uriPart}` | yes | yes | 
`$U{servlet:/foo?bar=$UE{$RA{bar}}}` |
 | | <a href="/site/apidocs/org/apache/juneau/rest/widget/Widget.html" 
target="_blank">Widget</a> | `$W{name}` | no | yes | `$W{MenuItemWidget}` |
 
+Transformation/conditional logic that used to be a dedicated `Var` class 
(`$UC{}`, `$IF{}`,
+`$SW{}`, `$PM{}`, `$PR{}`, `$PE{}`, `$NE{}`, `$CO{}`, `$LN{}`, `$ST{}`, 
`$LC{}`) is now expressed
+via the `#{name(args)}` function-call syntax introduced in 9.5.0. See
+[Simple Variable Language Basics — `#{...}` function-call 
syntax](/docs/topics/SimpleVariableLanguageBasics#-function-call-syntax-950)
+and the [9.5.0 release notes](/docs/release-notes/9.5.0) for the migration 
table.
+
 Custom variables can be defined by supplying a named `varResolver` bean via
 <a href="/site/apidocs/org/apache/juneau/rest/annotation/RestInject.html" 
target="_blank">@RestInject</a>.
 

Reply via email to