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 1b85a9530b TODO-351: add 9 additive APIs + full @Remote/@RemoteOp 
classic↔NG parity (Section B of the docs-sweep); fix @StringCtor null-context 
NPE; JDT cleanup; doc updates
1b85a9530b is described below

commit 1b85a9530bfb99520b2c18478518362a2114c39b
Author: James Bognar <[email protected]>
AuthorDate: Wed Aug 12 14:05:40 2026 -0400

    TODO-351: add 9 additive APIs + full @Remote/@RemoteOp classic↔NG parity 
(Section B of the docs-sweep); fix @StringCtor null-context NPE; JDT cleanup; 
doc updates
---
 pages/topics/02.03.JuneauCommonsLang.md      |  4 +-
 pages/topics/02.09.JuneauCommonsIO.md        | 43 +++++++++--------
 pages/topics/03.13.02.AutoSwaps.md           | 16 +++++++
 pages/topics/05.00.JuneauBean.md             |  2 +-
 pages/topics/05.04.JuneauBeanOpenApi3.md     |  5 +-
 pages/topics/05.06.JuneauBeanSwagger2.md     |  2 +-
 pages/topics/05.11.JuneauBeanJsonPatch.md    | 19 ++++++--
 pages/topics/07.02.00.BeanCentricTesting.md  | 10 ++--
 pages/topics/07.02.01.CustomErrorMessages.md |  6 +--
 pages/topics/07.02.02.00.Customization.md    | 10 ++--
 pages/topics/07.02.02.01.Stringifiers.md     | 12 ++---
 pages/topics/07.02.02.02.Listifiers.md       | 12 ++---
 pages/topics/07.02.02.03.Swappers.md         |  8 ++--
 pages/topics/07.02.03.PropertyExtractors.md  | 24 +++++-----
 pages/topics/10.20.StaticFiles.md            | 10 +++-
 pages/topics/10.21.StaticFilesMixin.md       | 12 +++--
 pages/topics/10.24.JspViewSupport.md         | 22 ++++++++-
 pages/topics/10.25.ThymeleafViewSupport.md   | 17 ++++++-
 pages/topics/10.26.MustacheViewSupport.md    | 50 ++++++++++++++++---
 pages/topics/10.27.FreemarkerViewSupport.md  | 17 ++++++-
 pages/topics/13.09.01.Remote.md              | 72 +++++++++++++++++++++++++++-
 pages/topics/13.09.02.RemoteMethod.md        | 37 +++++++++++++-
 pages/topics/13.14.NextGenRestClient.md      | 29 +++++++++++
 23 files changed, 351 insertions(+), 88 deletions(-)

diff --git a/pages/topics/02.03.JuneauCommonsLang.md 
b/pages/topics/02.03.JuneauCommonsLang.md
index 4108545fd3..03bfcb05f7 100644
--- a/pages/topics/02.03.JuneauCommonsLang.md
+++ b/pages/topics/02.03.JuneauCommonsLang.md
@@ -160,7 +160,7 @@ int comparison = v1.compareTo(v2);  // -1 (v1 < v2)
 Version range utility for checking if a version string falls within an 
OSGi-style range.
 
 ```java
-VersionRange range = new VersionRange("[1.0.0,2.0.0)");
+VersionRange range = VersionRange.of("[1.0.0,2.0.0)");
 boolean inRange = range.matches("1.5.0");  // true
 ```
 
@@ -224,7 +224,7 @@ String found = result.orElse("not found");
 
 ```java
 String current = "1.2.3";
-VersionRange supported = new VersionRange("[1.0.0,2.0.0)");
+VersionRange supported = VersionRange.of("[1.0.0,2.0.0)");
 
 if (supported.matches(current)) {
     // Version is supported
diff --git a/pages/topics/02.09.JuneauCommonsIO.md 
b/pages/topics/02.09.JuneauCommonsIO.md
index 4ef48e519f..a423d6e072 100644
--- a/pages/topics/02.09.JuneauCommonsIO.md
+++ b/pages/topics/02.09.JuneauCommonsIO.md
@@ -23,6 +23,11 @@ InputStream is2 = fsFile.read();
 LocalFile cachedFile = new LocalFile(MyClass.class, "template.html");
 cachedFile.cache();  // Cache contents in memory
 InputStream is3 = cachedFile.read();  // Fast - uses cache
+
+// Check existence without triggering an exception
+if (fsFile.exists()) {
+    InputStream is4 = fsFile.read();
+}
 ```
 
 ### <java-class><a 
href="/site/apidocs/org/apache/juneau/commons/io/LocalDir.html" 
target="_blank">LocalDir</a></java-class>
@@ -47,8 +52,9 @@ Fluent builder for creating `Reader` instances from files.
 
 ```java
 FileReaderBuilder builder = FileReaderBuilder.create()
-    .file("file.txt")
-    .charset(StandardCharsets.UTF_8);
+    .file(Paths.get("file.txt"))
+    .charset(StandardCharsets.UTF_8)
+    .allowNoFile(true);  // Don't throw if the file doesn't exist
 
 Reader reader = builder.build();
 ```
@@ -58,10 +64,10 @@ Fluent builder for creating `Writer` instances for files.
 
 ```java
 FileWriterBuilder builder = FileWriterBuilder.create()
-    .file("output.txt")
+    .file(Paths.get("output.txt"))
     .charset(StandardCharsets.UTF_8)
-    .append()
-    .buffered();
+    .append(true)
+    .buffered(true);
 
 Writer writer = builder.build();
 ```
@@ -72,7 +78,8 @@ Fluent builder for creating `Reader` instances from `Path` 
objects.
 ```java
 PathReaderBuilder builder = PathReaderBuilder.create()
     .path(Paths.get("file.txt"))
-    .charset(StandardCharsets.UTF_8);
+    .charset(StandardCharsets.UTF_8)
+    .allowNoFile(true);  // Don't throw if the file doesn't exist
 
 Reader reader = builder.build();
 ```
@@ -149,16 +156,14 @@ wrapped.close();  // Does not close the original writer
 ### Reading Files from Classpath or File System
 
 ```java
-// Try classpath first, then file system
+// Try classpath first, then file system - use exists() to check without
+// relying on exception handling for control flow.
 LocalFile file = new LocalFile(MyClass.class, "config.properties");
-try (InputStream is = file.read()) {
+LocalFile fsFile = new LocalFile(Paths.get("/etc/myapp/config.properties"));
+LocalFile resolved = file.exists() ? file : fsFile;
+
+try (InputStream is = resolved.read()) {
     // Read file contents
-} catch (IOException e) {
-    // Not found on the classpath - fall back to the file system
-    LocalFile fsFile = new 
LocalFile(Paths.get("/etc/myapp/config.properties"));
-    try (InputStream is = fsFile.read()) {
-        // Read file contents
-    }
 }
 ```
 
@@ -189,9 +194,9 @@ LocalFile about = templates.resolve("about.html");
 ### File I/O with Builders
 
 ```java
-// Read file with specific charset
+// Read file with specific charset, using a Path
 try (Reader reader = FileReaderBuilder.create()
-        .file("file.txt")
+        .file(Paths.get("file.txt"))
         .charset(StandardCharsets.UTF_8)
         .build()) {
     // Read from file
@@ -199,10 +204,10 @@ try (Reader reader = FileReaderBuilder.create()
 
 // Write file with append mode
 try (Writer writer = FileWriterBuilder.create()
-        .file("output.txt")
+        .file(Paths.get("output.txt"))
         .charset(StandardCharsets.UTF_8)
-        .append()
-        .buffered()
+        .append(true)
+        .buffered(true)
         .build()) {
     writer.write("New content");
 }
diff --git a/pages/topics/03.13.02.AutoSwaps.md 
b/pages/topics/03.13.02.AutoSwaps.md
index 1b5172f6c4..28aa319c62 100644
--- a/pages/topics/03.13.02.AutoSwaps.md
+++ b/pages/topics/03.13.02.AutoSwaps.md
@@ -27,6 +27,22 @@ Note that these methods cover conversion from several 
built-in Java types, meani
 - `parseString(String)` - <a 
href="https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/DatatypeConverter.html";
 target="_blank">DatatypeConverter</a>
 - `forName(String)` - <a 
href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html";
 target="_blank">Class</a>
 
+If your static factory method doesn't match any of those conventional names 
(e.g. `of(String)` or `create(String)`), you can use the <a 
href="/site/apidocs/org/apache/juneau/marshall/StringCtor.html" 
target="_blank">@StringCtor</a> annotation on the class to name the exact 
method to use, short-circuiting the name-based lookup above:
+
+```java
+@StringCtor("of")
+public class Money {
+
+    public static Money of(String value) {
+        return new Money(value);
+    }
+
+    ...
+}
+```
+
+Classes without `@StringCtor` continue to fall back to the conventional-name 
lookup (and then the single-`String`-arg constructor) described above.
+
 If you want to force a bean-like class to be serialized as a string, you can 
use the <a href="/site/apidocs/org/apache/juneau/marshall/Marshalled.html#as()" 
target="_blank">@Marshalled(as=STRING)</a> annotation on the class to force it 
to be serialized to a string using the `toString()` method.
 
 Serializing to other intermediate objects can be accomplished by defining a 
swap method directly on the class:
diff --git a/pages/topics/05.00.JuneauBean.md b/pages/topics/05.00.JuneauBean.md
index 641b6086bc..75ab98cf47 100644
--- a/pages/topics/05.00.JuneauBean.md
+++ b/pages/topics/05.00.JuneauBean.md
@@ -171,7 +171,7 @@ Swagger swagger = swagger()
             )
     )
     .addSchemes("http")
-    .addPath("/pet", "post",
+    .addOperation("/pet", "post",
         operation()
             .setTags("pet")
             .setSummary("Add a new pet to the store")
diff --git a/pages/topics/05.04.JuneauBeanOpenApi3.md 
b/pages/topics/05.04.JuneauBeanOpenApi3.md
index 53e0221edd..325f708c29 100644
--- a/pages/topics/05.04.JuneauBeanOpenApi3.md
+++ b/pages/topics/05.04.JuneauBeanOpenApi3.md
@@ -134,8 +134,9 @@ A few things to note when comparing this to the 
(superficially similar) Swagger
 [juneau-bean-swagger2](/docs/topics/JuneauBeanSwagger2):
 
 - OpenAPI v3 paths are added with `OpenApi.addPath(String path, PathItem 
pathItem)` — a 2-argument method that
-  takes a fully-built `PathItem`. This differs from Swagger v2's 
`Swagger.addPath(String path, String method,
-  Operation operation)`, a 3-argument method that adds a single operation 
directly. Individual HTTP methods on an
+  takes a fully-built `PathItem`. This differs from Swagger v2's 
`Swagger.addOperation(String path, String method,
+  Operation operation)` (also available under its original name, 
`Swagger.addPath(...)`, for backwards
+  compatibility), a 3-argument method that adds a single operation directly. 
Individual HTTP methods on an
   OpenAPI v3 `PathItem` are set via 
`setGet(Operation)`/`setPost(Operation)`/etc.
 - Request bodies use `RequestBodyInfo` (via the `requestBodyInfo()` factory), 
not `requestBody()`/`schema()`
   static factories — those don't exist in this package.
diff --git a/pages/topics/05.06.JuneauBeanSwagger2.md 
b/pages/topics/05.06.JuneauBeanSwagger2.md
index 7edf51ccbc..8e52b12272 100644
--- a/pages/topics/05.06.JuneauBeanSwagger2.md
+++ b/pages/topics/05.06.JuneauBeanSwagger2.md
@@ -46,7 +46,7 @@ Swagger swagger = swagger()
             )
     )
     .addSchemes("http")
-    .addPath("/pet", "post",
+    .addOperation("/pet", "post",
         operation()
             .setTags("pet")
             .setSummary("Add a new pet to the store")
diff --git a/pages/topics/05.11.JuneauBeanJsonPatch.md 
b/pages/topics/05.11.JuneauBeanJsonPatch.md
index aebae8ee39..dee30ebc82 100644
--- a/pages/topics/05.11.JuneauBeanJsonPatch.md
+++ b/pages/topics/05.11.JuneauBeanJsonPatch.md
@@ -33,7 +33,17 @@ This module models the JSON Patch wire format as typed 
`@Marshalled` POJOs with
 
 ## Serializer / parser configuration
 
-The dictionary wiring tells the parser how to map `op` ⇒ subclass, but the 
serializer still needs to know to emit the `op` discriminator. Configure both 
ends like this:
+The dictionary wiring tells the parser how to map `op` ⇒ subclass, but the 
serializer still needs to know to emit the `op` discriminator. 
`JsonPatchMarshaller` ships a pre-wired `DEFAULT` serializer/parser pair with 
that discriminator already configured, so callers don't need to hand-wire 
`typePropertyName(JsonPatchOperation.class,"op")` themselves:
+
+```java
+import org.apache.juneau.bean.jsonpatch.*;
+
+// Pre-configured serializer/parser, wired for the "op" discriminator.
+JsonSerializer ser = JsonPatchMarshaller.SERIALIZER;
+JsonParser par = JsonPatchMarshaller.PARSER;
+```
+
+If you need custom settings, build your own serializer/parser and still apply 
the same `typePropertyName` wiring:
 
 ```java
 import org.apache.juneau.marshall.json.*;
@@ -64,8 +74,9 @@ JsonPatch patch = new JsonPatch()
     .append(new MoveOp("/a/b/d", "/a/b/c"))
     .append(new CopyOp("/a/b/e", "/a/b/d"));
 
-String wire = ser.write(patch);
-JsonPatch back = par.read(wire, JsonPatch.class);
+// Static shortcuts on JsonPatchMarshaller use the pre-wired DEFAULT 
serializer/parser.
+String wire = JsonPatchMarshaller.of(patch);
+JsonPatch back = JsonPatchMarshaller.to(wire, JsonPatch.class);
 ```
 
 The wire form is a top-level JSON array of operation objects, matching the 
example in [RFC 6902 
§3](https://datatracker.ietf.org/doc/html/rfc6902#section-3):
@@ -86,7 +97,7 @@ The wire form is a top-level JSON array of operation objects, 
matching the examp
 The parser materialises the concrete subclass keyed off the wire `op` value. 
Each entry in the parsed `JsonPatch` is an instance of the matching subclass — 
no manual instanceof / switch needed:
 
 ```java
-JsonPatch back = par.read(wire, JsonPatch.class);
+JsonPatch back = JsonPatchMarshaller.to(wire, JsonPatch.class);
 for (JsonPatchOperation op : back) {
     if (op instanceof AddOp add)         { /* ... */ }
     else if (op instanceof RemoveOp rem) { /* ... */ }
diff --git a/pages/topics/07.02.00.BeanCentricTesting.md 
b/pages/topics/07.02.00.BeanCentricTesting.md
index abd3aac7bf..141e8dfbe7 100644
--- a/pages/topics/07.02.00.BeanCentricTesting.md
+++ b/pages/topics/07.02.00.BeanCentricTesting.md
@@ -559,7 +559,7 @@ For more details, see [Customization](Customization).
 
 ### Custom Bean Converters
 
-The default bean converter can be customized on a per-thread basis using 
`BctConfiguration.set(BeanConverter)` or via the `@BctConfig` annotation. This 
is particularly useful in test setup methods to configure a custom converter 
for all tests in a test class or method.
+The default bean converter can be customized on a per-thread basis using 
`BctAssertions.setConverter(BeanConverter)` (a thin passthrough to 
`BctConfiguration.set(BeanConverter)`) or via the `@BctConfig` annotation. This 
is particularly useful in test setup methods to configure a custom converter 
for all tests in a test class or method.
 
 ```java
 // Set custom converter in @BeforeEach method
@@ -567,12 +567,12 @@ The default bean converter can be customized on a 
per-thread basis using `BctCon
 void setUp() {
 var converter = BasicBeanConverter.builder()
     .defaultSettings()
-    .addStringifier(LocalDate.class, date -> 
+    .addStringifier(LocalDate.class, (conv,date) -> 
         date.format(DateTimeFormatter.ISO_LOCAL_DATE))
-    .addStringifier(Money.class, money -> 
+    .addStringifier(Money.class, (conv,money) -> 
         money.getAmount().toPlainString())
     .build();
-    BctConfiguration.set(converter);
+    setConverter(converter);
 }
 
 // All assertions now use the custom converter
@@ -584,7 +584,7 @@ void testWithCustomConverter() {
 // Reset in @AfterEach method
 @AfterEach
 void tearDown() {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
diff --git a/pages/topics/07.02.01.CustomErrorMessages.md 
b/pages/topics/07.02.01.CustomErrorMessages.md
index 10b1b8dc3f..b33adb1b8d 100644
--- a/pages/topics/07.02.01.CustomErrorMessages.md
+++ b/pages/topics/07.02.01.CustomErrorMessages.md
@@ -199,10 +199,10 @@ import static org.apache.juneau.commons.utils.Shorts.fs;
 void setUp() {
     var converter = BasicBeanConverter.builder()
         .defaultSettings()
-        .addStringifier(LocalDate.class, date -> 
+        .addStringifier(LocalDate.class, (conv,date) -> 
             date.format(DateTimeFormatter.ISO_LOCAL_DATE))
         .build();
-    BctConfiguration.set(converter);
+    setConverter(converter);
 }
 
 // Use custom message with the converter
@@ -211,7 +211,7 @@ assertBean(fs("Date validation failed for user %s", userId),
 
 @AfterEach
 void tearDown() {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
diff --git a/pages/topics/07.02.02.00.Customization.md 
b/pages/topics/07.02.02.00.Customization.md
index 640805d55a..8e5b5c29ad 100644
--- a/pages/topics/07.02.02.00.Customization.md
+++ b/pages/topics/07.02.02.00.Customization.md
@@ -248,10 +248,10 @@ public static class MyCustomConverter extends 
BasicBeanConverter {
         super(BasicBeanConverter.builder()
             .defaultSettings()
             // Add custom stringifier for LocalDate
-            .addStringifier(LocalDate.class, date -> 
+            .addStringifier(LocalDate.class, (conv,date) -> 
                 date.format(DateTimeFormatter.ISO_LOCAL_DATE))
             // Add custom stringifier for Money
-            .addStringifier(Money.class, money -> 
+            .addStringifier(Money.class, (conv,money) -> 
                 money.getAmount().toPlainString())
             .build());
     }
@@ -277,15 +277,15 @@ class MyTest {
 void setUp() {
     var converter = BasicBeanConverter.builder()
         .defaultSettings()
-        .addStringifier(LocalDate.class, date -> 
+        .addStringifier(LocalDate.class, (conv,date) -> 
             date.format(DateTimeFormatter.ISO_LOCAL_DATE))
         .build();
-    BctConfiguration.set(converter);
+    setConverter(converter); // BctAssertions.setConverter(...) - a thin 
passthrough to BctConfiguration.set(...)
 }
 
 @AfterEach
 void tearDown() {
-    BctConfiguration.clear(); // Also clears converter
+    resetConverter(); // BctAssertions.resetConverter() - a thin passthrough 
to BctConfiguration.clear()
 }
 ```
 
diff --git a/pages/topics/07.02.02.01.Stringifiers.md 
b/pages/topics/07.02.02.01.Stringifiers.md
index 87f6973934..56690e2162 100644
--- a/pages/topics/07.02.02.01.Stringifiers.md
+++ b/pages/topics/07.02.02.01.Stringifiers.md
@@ -109,11 +109,11 @@ var converter = BasicBeanConverter.builder()
     .build();
 
 // Usage in tests
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(order, "total,customer", "$99.99,{John Smith 
<joh***@example.com>}");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
@@ -148,18 +148,18 @@ Stringifier<TreeNode> treeStringifier = new 
Stringifier<TreeNode>() {
 // Create converter with custom formatting
 var converter = BasicBeanConverter.builder()
     .defaultSettings()
-    .addStringifier(LocalDate.class, date -> 
+    .addStringifier(LocalDate.class, (conv,date) -> 
         date.format(DateTimeFormatter.ISO_LOCAL_DATE))
-    .addStringifier(Money.class, money -> 
+    .addStringifier(Money.class, (conv,money) -> 
         money.getAmount().toPlainString())
     .build();
 
 // Use in assertions
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(order, "date,total", "2023-12-01,99.99");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
diff --git a/pages/topics/07.02.02.02.Listifiers.md 
b/pages/topics/07.02.02.02.Listifiers.md
index 81cfea53d9..fd25c7c9e3 100644
--- a/pages/topics/07.02.02.02.Listifiers.md
+++ b/pages/topics/07.02.02.02.Listifiers.md
@@ -146,22 +146,22 @@ private void collectDepthFirst(TreeNode node, 
List<Object> result) {
 ```java
 // Test paginated results
 PaginatedResult<User> page = userService.getUsers(pageNumber);
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertList(page, "Alice", "Bob", "Charlie");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 
 // Test database results
 ResultSet rs = statement.executeQuery("SELECT name FROM users");
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertList(rs, 
           predicate(row -> ((Map)row).get("name").equals("Alice")),
           predicate(row -> ((Map)row).get("name").equals("Bob")));
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
@@ -169,13 +169,13 @@ try {
 
 ```java
 // Test collection properties
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(paginatedResult, 
           "items{#{name}},totalCount", 
           "[{Alice},{Bob},{Charlie}],3");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
diff --git a/pages/topics/07.02.02.03.Swappers.md 
b/pages/topics/07.02.02.03.Swappers.md
index 1c9910aed8..d41c105e2e 100644
--- a/pages/topics/07.02.02.03.Swappers.md
+++ b/pages/topics/07.02.02.03.Swappers.md
@@ -178,7 +178,7 @@ assertBean(futureOrder, "id,total", "456,99.99");
 ```java
 // Test Result wrapper with custom swapper
 Result<User> result = userService.createUser(userData);
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(result, "name,email", "Alice,[email protected]");
 
@@ -190,7 +190,7 @@ ValidationResult<Order> validation = 
orderValidator.validate(order);
 ValidationResult<Order> invalidValidation = 
orderValidator.validate(invalidOrder);
     assertList(invalidValidation, "Missing required field: customer", "Invalid 
total: -10");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
@@ -199,11 +199,11 @@ ValidationResult<Order> invalidValidation = 
orderValidator.validate(invalidOrder
 ```java
 // Test lazy computation
 LazyValue<Report> lazyReport = new LazyValue<>(() -> generateReport());
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(lazyReport, "title,itemCount", "Monthly Report,150");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 
 // Swapper ensures the lazy value is evaluated before testing
diff --git a/pages/topics/07.02.03.PropertyExtractors.md 
b/pages/topics/07.02.03.PropertyExtractors.md
index 30b1d98ca3..754c4b0989 100644
--- a/pages/topics/07.02.03.PropertyExtractors.md
+++ b/pages/topics/07.02.03.PropertyExtractors.md
@@ -116,11 +116,11 @@ PropertyExtractor aliasExtractor = new 
PropertyExtractor() {
 };
 
 // Usage
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(user, "fname,lname,email", "John,Doe,[email protected]");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
@@ -151,12 +151,12 @@ PropertyExtractor computedExtractor = new 
PropertyExtractor() {
 };
 
 // Usage
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(user, "computed_fullName,computed_age,computed_initials", 
           "John Doe,30,J.D.");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
@@ -210,11 +210,11 @@ PropertyExtractor privateFieldExtractor = new 
PropertyExtractor() {
 };
 
 // Usage
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(myBean, "_privateField1,_privateField2", "value1,value2");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
@@ -276,12 +276,12 @@ PropertyExtractor configExtractor = new 
PropertyExtractor() {
 };
 
 // Usage
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(config, "database.host,database.port,app.name", 
           "localhost,5432,MyApp");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
@@ -447,7 +447,7 @@ PropertyExtractor fallbackExtractor = new 
PropertyExtractor() {
 ```java
 // Test database entity
 DatabaseEntity entity = loadEntity(123);
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(entity, "id,displayName,createdDate", "123,John 
Doe,2023-01-15");
 
@@ -460,7 +460,7 @@ Configuration config = loadConfig();
 User user = loadUser(456);
     assertBean(user, "computed_fullName,computed_age", "Alice Smith,28");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
@@ -468,12 +468,12 @@ User user = loadUser(456);
 
 ```java
 // Use property extractor with nested access
-BctConfiguration.set(converter);
+setConverter(converter);
 try {
     assertBean(order, "customer{computed_fullName},items{0{name}}", 
           "{John Doe},{{Laptop}}");
 } finally {
-    BctConfiguration.clear();
+    resetConverter();
 }
 ```
 
diff --git a/pages/topics/10.20.StaticFiles.md 
b/pages/topics/10.20.StaticFiles.md
index dee26eb29d..8d945738ba 100644
--- a/pages/topics/10.20.StaticFiles.md
+++ b/pages/topics/10.20.StaticFiles.md
@@ -8,11 +8,17 @@ The <a 
href="/site/apidocs/org/apache/juneau/rest/server/servlet/BasicRestServle
 ```java
 @RestGet(path="/htdocs/*")
 public HttpResource getHtdoc(RestRequest req, @Path("/*") String path, Locale 
locale) throws NotFound {
-    return req.getContext().getStaticFiles().resolve(path, 
locale).orElseThrow(NotFound::new);
+    return req.getStaticFiles().resolve(path, 
locale).orElseThrow(NotFound::new);
 }
 ```
 
-The static file finder can be accessed through the following method:
+The static file finder can be accessed through either of the following methods 
— the
+`RestRequest` shortcut is a convenience delegate for the `RestContext` 
accessor:
+
+<tree>
+<node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/rest/server/RestRequest.html" 
target="_blank">RestRequest</a></java-class></node-0>
+<node-1><java-method><a 
href="/site/apidocs/org/apache/juneau/rest/server/RestRequest.html#getStaticFiles()"
 target="_blank">getStaticFiles()</a></java-method></node-1>
+</tree>
 
 <tree>
 <node-0><java-class><a 
href="/site/apidocs/org/apache/juneau/rest/server/RestContext.html" 
target="_blank">RestContext</a></java-class></node-0>
diff --git a/pages/topics/10.21.StaticFilesMixin.md 
b/pages/topics/10.21.StaticFilesMixin.md
index ffd14603e0..6197ae2dd9 100644
--- a/pages/topics/10.21.StaticFilesMixin.md
+++ b/pages/topics/10.21.StaticFilesMixin.md
@@ -31,7 +31,7 @@ public class StaticFilesMixin {
         swagger = @OpSwagger(ignore = true)
     )
     public HttpResource getStaticFile(RestRequest req, @Path("/*") String 
path, Locale locale) {
-        return req.getContext().getStaticFiles().resolve(path, 
locale).orElseThrow(NotFound::new);
+        return req.getStaticFiles().resolve(path, 
locale).orElseThrow(NotFound::new);
     }
 
     @RestOp(
@@ -45,8 +45,10 @@ public class StaticFilesMixin {
 }
 ```
 
-The handler resolves the `StaticFiles` bean via 
`req.getContext().getStaticFiles()` at request time,
-which delegates to `BeanStore.getBean(StaticFiles.class)` and falls back to a 
default `BasicStaticFiles` instance
+The handler resolves the `StaticFiles` bean via `req.getStaticFiles()` at 
request time — a
+[`RestRequest`](/site/apidocs/org/apache/juneau/rest/server/RestRequest.html) 
convenience shortcut for
+`req.getContext().getStaticFiles()` — which delegates to 
`BeanStore.getBean(StaticFiles.class)` and falls
+back to a default `BasicStaticFiles` instance
 that searches the importer's classpath under `static/` and `htdocs/` plus the 
working-directory
 `static/` and `htdocs/` folders. Missing paths surface as `NotFound` thrown 
from the handler,
 which flows through the standard exception-rendering chain (RFC 7807 
problem-details when
@@ -155,8 +157,8 @@ public class MyResource extends RestServlet {
 }
 ```
 
-The `StaticFiles` bean is resolved through the request context via
-`RestContext.getStaticFiles()`, which honors the `@Bean`-factory walk 
(microservice
+The `StaticFiles` bean is resolved through the request context via 
`req.getStaticFiles()`
+(`RestContext.getStaticFiles()` under the hood), which honors the 
`@Bean`-factory walk (microservice
 path uses `BasicBeanStore`; Spring Boot path uses `SpringBeanStore` → 
`ApplicationContext.getBeanProvider(...)`).
 If you register multiple `StaticFiles` beans in a Spring `@Configuration`, 
mark exactly one
 `@Primary` so the bean store lookup is deterministic.
diff --git a/pages/topics/10.24.JspViewSupport.md 
b/pages/topics/10.24.JspViewSupport.md
index 6d2dee82be..0d7a731555 100644
--- a/pages/topics/10.24.JspViewSupport.md
+++ b/pages/topics/10.24.JspViewSupport.md
@@ -27,7 +27,7 @@ the mixin packaging makes this clean.
 | Class | Role |
 |---|---|
 | [`View`](/site/apidocs/org/apache/juneau/rest/server/view/View.html) (in 
`juneau-rest-server` core) | Engine-agnostic contract: `getTemplateName()`, 
`getAttributes()`, `getResponseHeaders()`. |
-| 
[`JspMixin`](/site/apidocs/org/apache/juneau/rest/server/view/jsp/JspMixin.html)
 | Mixin. Mounts `/jsp/*` for raw `.jsp` requests; registers `JspViewRenderer` 
on the response-processor chain. Builder: `basePath(String)` (default `/`). |
+| 
[`JspMixin`](/site/apidocs/org/apache/juneau/rest/server/view/jsp/JspMixin.html)
 | Mixin. Mounts `/jsp/*` for raw `.jsp` requests; registers `JspViewRenderer` 
on the response-processor chain. Builder: `basePath(String)` (default `/`), 
`cacheTemplates(boolean)` (accepted for 
[`ViewMixinBuilder`](/site/apidocs/org/apache/juneau/rest/server/view/ViewMixinBuilder.html)
 conformance; no-op — see below). |
 | 
[`JspView`](/site/apidocs/org/apache/juneau/rest/server/view/jsp/JspView.html) 
| `View` implementation. Immutable; fluent: 
`JspView.of("hello.jsp").attr("name", name).header("Cache-Control", 
"no-store")`. |
 | 
[`JspViewRenderer`](/site/apidocs/org/apache/juneau/rest/server/view/jsp/JspViewRenderer.html)
 | `ResponseProcessor` that detects `JspView` returns and dispatches via 
`ServletContext.getRequestDispatcher(...).forward(...)`. |
 
@@ -239,6 +239,26 @@ public class AdminViewsResource extends JspMixin {
 Both subclasses mount independently and each resolves templates against its own
 `basePath`.
 
+## View-mixin builder contract
+
+`JspMixin.Builder` implements
+[`ViewMixinBuilder`](/site/apidocs/org/apache/juneau/rest/server/view/ViewMixinBuilder.html),
 the
+minimal contract shared by all four view-engine mixin builders 
(`basePath(String)` +
+`cacheTemplates(boolean)`):
+
+```java
+import org.apache.juneau.rest.server.view.*;
+
+ViewMixinBuilder<?> anyEngineBuilder = JspMixin.create();
+anyEngineBuilder.basePath("/WEB-INF/views/").cacheTemplates(false);
+```
+
+`cacheTemplates(boolean)` is accepted and reported back (`isCacheTemplates()`) 
purely for
+conformance with the shared contract — it has **no effect** on this bridge. 
JSP recompilation is
+delegated entirely to the servlet container via 
`RequestDispatcher.forward(...)`; there is no
+bridge-owned template cache to disable. Configure hot-reload through your 
container instead (e.g.
+Tomcat/Jasper's `development` / `modificationTestInterval` 
servlet-init-params).
+
 ## Limitations and out-of-scope
 
 - **JSF / Facelets** — not planned. JSF is a separate concern; if ever 
requested, ships
diff --git a/pages/topics/10.25.ThymeleafViewSupport.md 
b/pages/topics/10.25.ThymeleafViewSupport.md
index 3d13d640e3..8589a89c11 100644
--- a/pages/topics/10.25.ThymeleafViewSupport.md
+++ b/pages/topics/10.25.ThymeleafViewSupport.md
@@ -29,7 +29,7 @@ It also has zero servlet-container dependencies: the core 
engine renders directl
 | Class | Role |
 |---|---|
 | [`View`](/site/apidocs/org/apache/juneau/rest/server/view/View.html) (in 
`juneau-rest-server` core) | Engine-agnostic contract: `getTemplateName()`, 
`getAttributes()`, `getResponseHeaders()`. |
-| 
[`ThymeleafMixin`](/site/apidocs/org/apache/juneau/rest/server/view/thymeleaf/ThymeleafMixin.html)
 | Mixin. Mounts `/thymeleaf/*` for raw `.html` template requests; registers 
`ThymeleafViewRenderer` on the response-processor chain. Builder: 
`basePath(String)` (default `/`), `cacheTemplates(boolean)` (default `true`), 
`templateMode(TemplateMode)` (default `HTML`). |
+| 
[`ThymeleafMixin`](/site/apidocs/org/apache/juneau/rest/server/view/thymeleaf/ThymeleafMixin.html)
 | Mixin. Mounts `/thymeleaf/*` for raw `.html` template requests; registers 
`ThymeleafViewRenderer` on the response-processor chain. Builder: 
`basePath(String)` (default `/`), `cacheTemplates(boolean)` (default `true`), 
`templateMode(TemplateMode)` (default `HTML`). Builder implements the shared 
[`ViewMixinBuilder`](/site/apidocs/org/apache/juneau/rest/server/view/ViewMixinBuilder.html)
 c [...]
 | 
[`ThymeleafView`](/site/apidocs/org/apache/juneau/rest/server/view/thymeleaf/ThymeleafView.html)
 | `View` implementation. Immutable; fluent: 
`ThymeleafView.of("hello").attr("name", name).header("Cache-Control", 
"no-store")`. |
 | 
[`ThymeleafViewRenderer`](/site/apidocs/org/apache/juneau/rest/server/view/thymeleaf/ThymeleafViewRenderer.html)
 | `ResponseProcessor` that detects `ThymeleafView` returns and asks the 
configured `org.thymeleaf.TemplateEngine` to `process(templateName, context, 
writer)` directly onto the response. |
 
@@ -243,6 +243,21 @@ public class AdminViewsResource extends ThymeleafMixin {
 
 Both subclasses mount independently and each resolves templates against its 
own `basePath`.
 
+## View-mixin builder contract
+
+`ThymeleafMixin.Builder` implements
+[`ViewMixinBuilder`](/site/apidocs/org/apache/juneau/rest/server/view/ViewMixinBuilder.html),
 the
+minimal contract shared by all four view-engine mixin builders 
(`basePath(String)` +
+`cacheTemplates(boolean)`). Code written against the interface works unchanged 
if you later swap
+engines:
+
+```java
+import org.apache.juneau.rest.server.view.*;
+
+ViewMixinBuilder<?> anyEngineBuilder = ThymeleafMixin.create();
+anyEngineBuilder.basePath("/templates/").cacheTemplates(false);
+```
+
 ## Limitations and out-of-scope
 
 - **`thymeleaf-spring6` is not a direct dep of the bridge module.** The bridge 
depends only
diff --git a/pages/topics/10.26.MustacheViewSupport.md 
b/pages/topics/10.26.MustacheViewSupport.md
index 4ad16ef88f..36707bcec4 100644
--- a/pages/topics/10.26.MustacheViewSupport.md
+++ b/pages/topics/10.26.MustacheViewSupport.md
@@ -32,7 +32,7 @@ JavaScript / Go / Python / Ruby front ends already use.
 | Class | Role |
 |---|---|
 | [`View`](/site/apidocs/org/apache/juneau/rest/server/view/View.html) (in 
`juneau-rest-server` core) | Engine-agnostic contract: `getTemplateName()`, 
`getAttributes()`, `getResponseHeaders()`. |
-| 
[`MustacheMixin`](/site/apidocs/org/apache/juneau/rest/server/view/mustache/MustacheMixin.html)
 | Mixin. Mounts `/mustache/*` for raw `.mustache` template requests; registers 
`MustacheViewRenderer` on the response-processor chain. Builder: 
`basePath(String)` (default `/`), `templateSuffix(String)` (default 
`.mustache`). |
+| 
[`MustacheMixin`](/site/apidocs/org/apache/juneau/rest/server/view/mustache/MustacheMixin.html)
 | Mixin. Mounts `/mustache/*` for raw `.mustache` template requests; registers 
`MustacheViewRenderer` on the response-processor chain. Builder: 
`basePath(String)` (default `/`), `templateSuffix(String)` (default 
`.mustache`), `cacheTemplates(boolean)` (default `true`). Builder implements 
the shared 
[`ViewMixinBuilder`](/site/apidocs/org/apache/juneau/rest/server/view/ViewMixinBuilder.html)
 c [...]
 | 
[`MustacheView`](/site/apidocs/org/apache/juneau/rest/server/view/mustache/MustacheView.html)
 | `View` implementation. Immutable; fluent: 
`MustacheView.of("hello").attr("name", name).header("Cache-Control", 
"no-store")`. |
 | 
[`MustacheViewRenderer`](/site/apidocs/org/apache/juneau/rest/server/view/mustache/MustacheViewRenderer.html)
 | `ResponseProcessor` that detects `MustacheView` returns and asks the 
configured `com.github.mustachejava.MustacheFactory` to compile and execute the 
template directly onto the response writer. |
 
@@ -173,6 +173,49 @@ The default mount `/mustache/*` can be overridden via the 
SVL variable
 `Config` key (`juneau.mustache.path = views`) to change the runtime mount 
without
 subclassing.
 
+## Template caching
+
+`Builder#cacheTemplates(boolean)` (default `true`) controls whether the 
bridge-default
+`MustacheFactory` (built lazily when no `@Bean MustacheFactory` is registered) 
caches compiled
+templates:
+
+```java
+@Bean
+MustacheMixin mustache() {
+    return MustacheMixin.create()
+        .basePath("/templates/")
+        .cacheTemplates(false)   // dev hot-reload: rebuild the factory on 
every render
+        .build();
+}
+```
+
+- `true` (default, production-safe) — the bridge builds the default factory 
once and reuses it;
+  mustache.java's own `DefaultMustacheFactory` compiles-and-caches templates 
internally, so a
+  template is compiled exactly once.
+- `false` — the bridge rebuilds the default factory (with an empty 
compile-cache) on every
+  render, so edited `.mustache` files under `basePath` are picked up without a 
server restart.
+
+The flag only affects the bridge-default factory; a user-supplied `@Bean 
MustacheFactory`
+manages its own caching and is returned as-is regardless of this setting. This 
is the same knob
+the sibling JSP / Thymeleaf / FreeMarker bridges expose via the shared
+[`ViewMixinBuilder`](/site/apidocs/org/apache/juneau/rest/server/view/ViewMixinBuilder.html)
+contract — see [View-mixin builder contract](#view-mixin-builder-contract) 
below.
+
+## View-mixin builder contract
+
+`MustacheMixin.Builder` implements
+[`ViewMixinBuilder`](/site/apidocs/org/apache/juneau/rest/server/view/ViewMixinBuilder.html),
 the
+minimal contract shared by all four view-engine mixin builders 
(`basePath(String)` +
+`cacheTemplates(boolean)`). Code written against the interface works unchanged 
if you later swap
+engines:
+
+```java
+import org.apache.juneau.rest.server.view.*;
+
+ViewMixinBuilder<?> anyEngineBuilder = MustacheMixin.create();
+anyEngineBuilder.basePath("/templates/").cacheTemplates(false);
+```
+
 ## Path-traversal protection
 
 The raw-template handler funnels every user-supplied `@Path("/*") String path` 
through
@@ -255,11 +298,6 @@ Both subclasses mount independently and each resolves 
templates against its own
   a different API entirely. The bridge module targets `mustache.java`'s 
`MustacheFactory`.
   Apps preferring `jmustache` can wire a custom `MustacheFactory` `@Bean` that 
adapts the
   `jmustache` API, but the bridge does not ship that adapter.
-- **No `cacheTemplates(boolean)` knob in 9.5.0.** `DefaultMustacheFactory` 
caches compiled
-  templates indefinitely and does not expose a runtime disable switch. Apps 
that need
-  hot-reload during development should register a custom `MustacheFactory` 
`@Bean` that
-  evicts on its own (e.g. a `DefaultMustacheFactory` rebuilt per request, or
-  `InvalidatingMustacheFactory`). Tracked for a follow-on enhancement.
 - **Lambda sections are users' responsibility.** Mustache's lambda-section 
spec lets a
   template attribute resolve to a function; pass the lambda as an attribute via
   `MustacheView.attr("greet", new Mustache.Lambda() { ... })` exactly as you 
would directly
diff --git a/pages/topics/10.27.FreemarkerViewSupport.md 
b/pages/topics/10.27.FreemarkerViewSupport.md
index 1beb8dd7f0..cacdd513f5 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`). |
+| 
[`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
 [...]
 | 
[`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. |
 
@@ -288,6 +288,21 @@ public class AdminViewsResource extends FreemarkerMixin {
 Both subclasses mount independently and each resolves templates against its own
 `basePath`.
 
+## View-mixin builder contract
+
+`FreemarkerMixin.Builder` implements
+[`ViewMixinBuilder`](/site/apidocs/org/apache/juneau/rest/server/view/ViewMixinBuilder.html),
 the
+minimal contract shared by all four view-engine mixin builders 
(`basePath(String)` +
+`cacheTemplates(boolean)`). Code written against the interface works unchanged 
if you later swap
+engines:
+
+```java
+import org.apache.juneau.rest.server.view.*;
+
+ViewMixinBuilder<?> anyEngineBuilder = FreemarkerMixin.create();
+anyEngineBuilder.basePath("/templates/").cacheTemplates(false);
+```
+
 ## Limitations and out-of-scope
 
 - **No bridge-default `Configuration` mutation for user beans.** The
diff --git a/pages/topics/13.09.01.Remote.md b/pages/topics/13.09.01.Remote.md
index 8b062425a8..12b878a160 100644
--- a/pages/topics/13.09.01.Remote.md
+++ b/pages/topics/13.09.01.Remote.md
@@ -105,6 +105,13 @@ public class MyHeaderList extends HeaderList {
 ```
 :::
 
+:::note Engine-specific
+`headerList` is honored only by the **classic** proxy engine 
(`RestClient.getRemote(...)`). Its value type is the
+classic Apache-HttpClient `HeaderList`, which is transport-specific, so the 
next-generation engine cannot honor it and
+emits a one-time build-time warning if it is set. Use `headers` for 
transport-agnostic constant headers that work on
+both engines.
+:::
+
 ## @Remote(version/versionHeader)
 
 The <a 
href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#version()" 
target="_blank">@Remote(version)</a> and <a 
href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#versionHeader()" 
target="_blank">@Remote(versionHeader)</a> annotations are used to specify the 
client-side version of this interface that can be used on the server side to 
perform version-specific handling.
@@ -138,4 +145,67 @@ public Object foo()  {...}
 
 :::info See Also
 [Client Versioning](/docs/topics/ClientVersioning)
-:::
\ No newline at end of file
+:::
+
+## Request-policy members
+
+`@Remote` also supports a set of interface-level request-policy members. As of 
10.0.0 these are honored by **both**
+the classic (`RestClient.getRemote(...)`) and next-generation proxy engines — 
previously several of them were silently
+ignored by the classic engine.
+
+| Member | Purpose |
+|---|---|
+| <a href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#baseUrl()" 
target="_blank">baseUrl</a> | A base/host override applied to every operation 
(in place of the client root URL). Must use the `http`/`https` scheme. |
+| <a href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#accept()" 
target="_blank">accept</a> | Default `Accept` header (and response-parser 
fallback) for every operation. |
+| <a 
href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#contentType()" 
target="_blank">contentType</a> | Default `Content-Type` header (and 
request-serializer selector) for every operation. |
+| <a 
href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#queryData()" 
target="_blank">queryData</a> | Constant `"name=value"` query parameters added 
to every request. |
+| <a href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#formData()" 
target="_blank">formData</a> | Constant `"name=value"` form-data parameters 
added to every request. |
+| <a href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#timeout()" 
target="_blank">timeout</a> | Per-call read/socket timeout as a duration string 
(e.g. `"30s"`, `"500ms"`). |
+| <a href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#retries()" 
target="_blank">retries</a> | Max automatic retry attempts for safe 
(idempotent) operations on transport failures and retryable statuses 
(`429`/`5xx`). |
+| <a 
href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#retryNonIdempotent()"
 target="_blank">retryNonIdempotent</a> | Opt non-idempotent verbs 
(`POST`/`PATCH`) into automatic retries. |
+| <a 
href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#throwOnError()" 
target="_blank">throwOnError</a> | Controls error handling for `>= 400` 
responses (see below). |
+
+Every one of these can also be set per-operation on 
[@RemoteOp](/docs/topics/RemoteMethod) (and the
+`@RemoteGet`/`@RemotePost`/... variants). The method-level value overrides the 
interface-level value, and the constant
+`headers`/`queryData`/`formData` entries are merged (method-level entries win 
on a name collision).
+
+:::tip Example
+```java
+@Remote(
+    path="/petstore",
+    accept="application/json",
+    timeout="30s",
+    retries=3,
+    queryData="apiKey=$S{petstore.apiKey}"
+)
+public interface PetStore {...}
+```
+:::
+
+### throwOnError
+
+By default (`throwOnError=false`) a proxy call lets an HTTP error response 
(status `>= 400`) flow through:
+
+- If the method declares a matching HTTP-response exception in its `throws` 
clause (e.g. `throws NotFound` for a
+  `404`), that typed exception is thrown.
+- Otherwise the error body is returned/parsed as the normal return value.
+
+Setting `throwOnError=true` additionally throws a generic
+<a 
href="/site/apidocs/org/apache/juneau/http/response/BasicHttpException.html" 
target="_blank">BasicHttpException</a>
+for any unmatched `>= 400` response.
+
+:::note
+Methods returning `void` (or `@RemoteOp(returns=STATUS)`) never throw on an 
error status — a `void` method discards
+the response entirely and a `STATUS` method reports the code as its return 
value.
+:::
+
+## Engine parity
+
+As of 10.0.0 every `@Remote`/`@RemoteOp` member is honored by both proxy 
engines, **except** for two members that are
+genuinely engine-specific. Rather than silently ignore them, the proxy emits a 
one-time build-time warning when the
+member is set on the engine that cannot honor it:
+
+| Member | Honored by | On the other engine |
+|---|---|---|
+| <a 
href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#interceptors()" 
target="_blank">interceptors</a> | next-generation | The **classic** engine 
emits a build-time warning — the classic and next-generation 
`RestCallInterceptor` SPI types are nominally incompatible (a single class 
cannot implement both). |
+| <a 
href="/site/apidocs/org/apache/juneau/http/remote/Remote.html#headerList()" 
target="_blank">headerList</a> | classic | The **next-generation** engine emits 
a build-time warning — its value type is the classic transport-specific 
`HeaderList`. Use `headers` for transport-agnostic constant headers. |
\ No newline at end of file
diff --git a/pages/topics/13.09.02.RemoteMethod.md 
b/pages/topics/13.09.02.RemoteMethod.md
index c0bc2325ba..29781cc705 100644
--- a/pages/topics/13.09.02.RemoteMethod.md
+++ b/pages/topics/13.09.02.RemoteMethod.md
@@ -87,7 +87,8 @@ The return type of the Java methods can be any of the 
following:
 
 - **void/<a 
href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Void.html";
 target="_blank">Void</a>**
   - Don't parse any response.
-  - Note that the method will still throw a runtime exception if an error HTTP 
status is returned.
+  - The response is discarded entirely, so a `void` method never throws on an 
error HTTP status. Use a non-void return
+    type (with a declared HTTP-response exception in the `throws` clause, or 
`throwOnError`) if you need error handling.
 
 - **Any parseable POJO**
   - The body of the response will be converted to the POJO using the parser 
defined on the `RestClient` based on the `Content-Type` of the response.
@@ -124,3 +125,37 @@ public interface PetStore {
 
 If your `RestClient` does not have a parser associated with it, then the value 
is converted directly from a String using
 the rules defined in [POJO Categories](/docs/topics/PojoCategories).
+
+## Method-level request policy
+
+In addition to `method`/`path`/`returns`, `@RemoteOp` (and the 
`@RemoteGet`/`@RemotePost`/`@RemotePut`/`@RemotePatch`/`@RemoteDelete`
+variants) supports the same request-policy members as 
[@Remote](/docs/topics/Remote): `headers`, `queryData`, `formData`,
+`accept`, `contentType`, `baseUrl`, `timeout`, `retries`, 
`retryNonIdempotent`, and `throwOnError`. A method-level value
+overrides the interface-level value, and constant 
`headers`/`queryData`/`formData` entries are merged with the
+interface-level entries (method-level wins on a name collision). As of 10.0.0 
these are honored by both the classic and
+next-generation proxy engines.
+
+An additional call-time endpoint override is available via the
+<a href="/site/apidocs/org/apache/juneau/http/Url.html" 
target="_blank">@Url</a> parameter annotation, which replaces the
+resolved URL for that single call (it takes precedence over `baseUrl`, and — 
like `baseUrl` — must resolve to an
+`http`/`https` URL).
+
+:::tip Example
+```java
+@Remote(path="/petstore")
+public interface PetStore {
+
+    // Per-operation Accept + retry policy, overriding any interface-level 
defaults.
+    @RemoteGet(path="/pets", accept="application/json", retries=3)
+    Pet[] getPets();
+
+    // Call-time endpoint override.
+    @RemoteGet
+    Pet[] getPetsFrom(@Url String url);
+}
+```
+:::
+
+:::info See Also
+[@Remote](/docs/topics/Remote) - Interface-level request policy and 
cross-engine parity notes.
+:::
diff --git a/pages/topics/13.14.NextGenRestClient.md 
b/pages/topics/13.14.NextGenRestClient.md
index c317c76b9b..bb41e6f925 100644
--- a/pages/topics/13.14.NextGenRestClient.md
+++ b/pages/topics/13.14.NextGenRestClient.md
@@ -151,6 +151,35 @@ call, and `run()` transfers ownership of the connection to 
the returned `RestRes
 
 ---
 
+## Typed Response Header Accessors
+
+`ResponseHeader` (obtained via `RestResponse.header(String)`) has a small set 
of typed-wrapper
+convenience methods, mirroring the classic stack's `asXxxHeader()` family 
without pulling in any
+`org.apache.http.classic.*` types. Each is a thin parse over the header's raw 
string value using
+JDK types or the transport-neutral types already in `juneau-rest-common`:
+
+| Method | Return type | Absent / unparseable |
+|---|---|---|
+| `asBoolean()` | `Optional<Boolean>` | empty |
+| `asZonedDateTime()` | `Optional<ZonedDateTime>` | empty (RFC-1123 HTTP date) 
|
+| `asUri()` | `Optional<URI>` | empty |
+| `asEntityTag()` | `Optional<EntityTag>` | empty (e.g. `ETag`) |
+| `asEntityTags()` | `EntityTags` (never `null`) | `EntityTags.EMPTY` (e.g. 
`If-Match`) |
+| `asStringRanges()` | `StringRanges` (never `null`) | empty (e.g. 
`Accept-Encoding`) |
+
+```java
+try (var resp = client.get("/users/123").run()) {
+    Optional<ZonedDateTime> lastModified = 
resp.header("Last-Modified").asZonedDateTime();
+    Optional<EntityTag> etag = resp.header("ETag").asEntityTag();
+    EntityTags ifMatch = resp.header("If-Match").asEntityTags();
+}
+```
+
+These sit alongside the existing generic accessors (`asInteger()`, `asLong()`, 
`asOptional()`,
+`asCsvList()`); for anything not covered here, parse `getValue()` directly.
+
+---
+
 ## Declarative Remote-Proxy Features (Next-Gen)
 
 The next-generation engine adds a family of declarative capabilities to the

Reply via email to