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 5ed663e4b4 Fix JDK 25 test flush fragility in
juneau-rest-client-classic; bump Micrometer to 1.15.12 (CVE-2026-40984);
restore coverage.py --by-module
5ed663e4b4 is described below
commit 5ed663e4b46b074daca1248c4a10b064c230b711
Author: James Bognar <[email protected]>
AuthorDate: Wed Aug 12 12:26:30 2026 -0400
Fix JDK 25 test flush fragility in juneau-rest-client-classic; bump
Micrometer to 1.15.12 (CVE-2026-40984); restore coverage.py --by-module
---
pages/topics/01.00.JuneauEcosystemOverview.md | 11 +-
pages/topics/01.02.WhyJuneau.md | 2 +-
pages/topics/02.02.JuneauCommonsCollections.md | 18 +--
pages/topics/02.03.JuneauCommonsLang.md | 52 ++++-----
pages/topics/02.06.JuneauCommonsSettings.md | 22 +++-
pages/topics/02.09.JuneauCommonsIO.md | 53 +++++----
pages/topics/03.03.01.JavaBeansSupport.md | 12 +-
pages/topics/03.03.02.JavaRecordsSupport.md | 24 ++--
pages/topics/03.03.06.BeanIgnoreAnnotation.md | 2 +-
pages/topics/03.13.00.Swaps.md | 8 +-
pages/topics/03.13.03.PerMediaTypeSwaps.md | 6 +-
pages/topics/03.39.00.JsonlSupport.md | 6 +-
pages/topics/03.42.Bson.md | 6 +-
pages/topics/03.44.HoconSupport.md | 6 +-
pages/topics/03.47.06.LargeDatasetStreaming.md | 9 +-
pages/topics/05.04.JuneauBeanOpenApi3.md | 14 +--
pages/topics/07.02.01.CustomErrorMessages.md | 4 +-
pages/topics/07.02.02.01.Stringifiers.md | 8 +-
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.05.Marshalling.md | 6 +-
pages/topics/10.20.StaticFiles.md | 11 ++
pages/topics/10.21.StaticFilesMixin.md | 13 ++-
pages/topics/10.22.ConventionEndpointsMixins.md | 10 +-
pages/topics/10.28.ClientVersioning.md | 1 +
.../topics/10.32.RestServerLoggingAndDebugging.md | 71 ++++++++----
pages/topics/10.38.RestServerAuthGuards.md | 18 +--
pages/topics/13.16.StreamingCursors.md | 49 ++++----
pages/topics/15.05.VarResolver.md | 16 +--
pages/topics/18.02.ScClientOverview.md | 10 +-
pages/topics/20.JuneauExamples.md | 36 +++---
pages/topics/23.02.JuneauShadedRestClient.md | 125 +++++++++++----------
pages/topics/23.03.JuneauShadedRestServer.md | 20 ++--
pages/topics/23.05.JuneauShadedAll.md | 40 +++----
src/pages/downloads.md | 29 ++---
36 files changed, 430 insertions(+), 332 deletions(-)
diff --git a/pages/topics/01.00.JuneauEcosystemOverview.md
b/pages/topics/01.00.JuneauEcosystemOverview.md
index 7a7048f96f..673daa2bee 100644
--- a/pages/topics/01.00.JuneauEcosystemOverview.md
+++ b/pages/topics/01.00.JuneauEcosystemOverview.md
@@ -54,7 +54,8 @@ The Juneau ecosystem consists of the following parts:
| [juneau‑bean‑jsonapi](/docs/topics/JuneauBeanJsonApi) | DTO beans for the
JSON:API format | • *None* |
| [juneau‑bean‑jsonpatch](/docs/topics/JuneauBeanJsonPatch) | DTO beans for
JSON Patch (RFC 6902) | • *None* |
| [juneau‑bean‑jsonschema](/docs/topics/JuneauBeanJsonSchema) | DTO beans for
JSON Schema | • *None* |
-| [juneau‑bean‑mcp‑2025‑06‑18](/docs/topics/JuneauBeanMcp) | DTO beans for the
Model Context Protocol (MCP) revision `2025-06-18` | • *None* |
+| [juneau‑bean‑mcp‑v20250618](/docs/topics/JuneauBeanMcp) | DTO beans for the
Model Context Protocol (MCP) revision `2025-06-18` | • *None* |
+| [juneau‑bean‑mcp‑v20260728](/docs/topics/JuneauBeanMcp) | DTO beans for the
Model Context Protocol (MCP) revision `2026-07-28` (current) | • *None* |
| [juneau‑bean‑openapi‑v3](/docs/topics/JuneauBeanOpenApi3) | DTO beans for
OpenAPI v3 | • *None* |
| [juneau‑bean‑rfc7807](/docs/topics/JuneauBeanRfc7807) | DTO beans for RFC
7807 Problem Details | • *None* |
| [juneau‑bean‑swagger‑v2](/docs/topics/JuneauBeanSwagger2) | DTO beans for
Swagger / OpenAPI v2 | • *None* |
@@ -63,7 +64,9 @@ The Juneau ecosystem consists of the following parts:
|
[juneau‑rest‑common‑classic](https://github.com/apache/juneau/tree/master/juneau-rest/juneau-rest-common-classic)
| REST Common — Apache HttpClient 4.5-compatible facades | • Apache HttpClient
4.5 |
| [juneau‑rest‑server](/docs/topics/JuneauRestServer) | REST Servlet API | •
Servlet 5.0+ |
|
[juneau‑rest‑server‑rdf](https://github.com/apache/juneau/tree/master/juneau-rest/juneau-rest-server-rdf)
| REST Server RDF content-type support | • Apache Jena 5.x |
-| [juneau‑rest‑server‑mcp](/docs/topics/JuneauRestServerMcp) | Model Context
Protocol endpoint | • *None* |
+| [juneau‑rest‑server‑mcp](/docs/topics/JuneauRestServerMcp) | Model Context
Protocol endpoint (revision-neutral core) | • *None* |
+| [juneau‑rest‑server‑mcp‑v20250618](/docs/topics/JuneauRestServerMcp) | MCP
endpoint adapter for revision `2025-06-18` | • *None* |
+| [juneau‑rest‑server‑mcp‑v20260728](/docs/topics/JuneauRestServerMcp) | MCP
endpoint adapter for revision `2026-07-28` (current) | • *None* |
| [juneau‑rest‑server‑auth‑jwt](/docs/topics/AuthFilterFramework) | JWT
bearer-token validation (opt-in) | • nimbus-jose-jwt |
| [juneau‑rest‑server‑auth‑saml](/docs/topics/SamlAuthSupport) | SAML 2.0
assertion validation (opt-in) | • OpenSAML |
| [juneau‑rest‑server‑auth‑oauth](/docs/topics/OAuthAuthSupport) | OAuth 2.0 /
OIDC validation and client flows (opt-in) | • Nimbus SDK |
@@ -84,6 +87,10 @@ The Juneau ecosystem consists of the following parts:
|
[juneau‑rest‑client‑apache‑httpclient‑50](https://github.com/apache/juneau/tree/master/juneau-rest/juneau-rest-client-apache-httpclient-50)
| Apache HttpClient 5.x transport adapter | • Apache HttpClient 5.x |
|
[juneau‑rest‑client‑okhttp](https://github.com/apache/juneau/tree/master/juneau-rest/juneau-rest-client-okhttp)
| OkHttp 5.x transport adapter | • OkHttp 5.x |
|
[juneau‑rest‑client‑jetty](https://github.com/apache/juneau/tree/master/juneau-rest/juneau-rest-client-jetty)
| Jetty 12 HttpClient transport adapter | • Jetty 12 |
+| [juneau‑rest‑client‑mcp](/docs/topics/JuneauRestClientMcp) | Model Context
Protocol client core (revision-neutral) | • *None* |
+| [juneau‑rest‑client‑mcp‑v20250618](/docs/topics/JuneauRestClientMcp) | MCP
client adapter for revision `2025-06-18` | • *None* |
+| [juneau‑rest‑client‑mcp‑v20260728](/docs/topics/JuneauRestClientMcp) | MCP
client adapter for revision `2026-07-28` (current) | • *None* |
+| [juneau‑rest‑client‑mcp‑auth](/docs/topics/JuneauRestClientMcp) | MCP
client-side OAuth 2.1 token acquisition (opt-in) | • Nimbus SDK |
| [juneau‑rest‑mock](/docs/topics/JuneauRestMock) | REST Testing API | •
*None* |
| **juneau-microservice** | | |
| [juneau‑microservice](/docs/topics/JuneauMicroservice) | Microservice base
framework | • *None* |
diff --git a/pages/topics/01.02.WhyJuneau.md b/pages/topics/01.02.WhyJuneau.md
index f63436316e..f925812b77 100644
--- a/pages/topics/01.02.WhyJuneau.md
+++ b/pages/topics/01.02.WhyJuneau.md
@@ -216,7 +216,7 @@ try (TokenReader r = Json.DEFAULT.readTokens(inputStream)) {
- **Automatic Documentation:** Swagger UI generated automatically from your
code
- **Content Negotiation:** Support multiple formats with zero additional
configuration
- **Type Safety:** Compile-time checking for REST client interfaces
-- **MCP Support:** `juneau-rest-server-mcp` (revision-neutral core) plus the
`juneau-rest-server-mcp-v20250618` adapter expose any `@Rest` resource as a
Model Context Protocol (MCP) endpoint, enabling LLM tool-calling with no
additional framework
+- **MCP Support:** `juneau-rest-server-mcp` (revision-neutral core) plus the
`juneau-rest-server-mcp-v20260728` (current) and
`juneau-rest-server-mcp-v20250618` adapters expose any `@Rest` resource as a
Model Context Protocol (MCP) endpoint, enabling LLM tool-calling with no
additional framework — client-side MCP support (`juneau-rest-client-mcp` and
its revision adapters) is also available
## When to Choose Juneau
diff --git a/pages/topics/02.02.JuneauCommonsCollections.md
b/pages/topics/02.02.JuneauCommonsCollections.md
index fa4673b8d9..0908980d0d 100644
--- a/pages/topics/02.02.JuneauCommonsCollections.md
+++ b/pages/topics/02.02.JuneauCommonsCollections.md
@@ -16,24 +16,24 @@ Fluent builder for constructing `List` instances with
various configuration opti
import static org.apache.juneau.commons.utils.CollectionUtils.*;
// Basic usage
-List<String> list = listb(String.class)
+List<String> list = listBuilder(String.class)
.add("apple", "banana", "cherry")
.build();
// With sorting
-List<Integer> sorted = listb(Integer.class)
+List<Integer> sorted = listBuilder(Integer.class)
.add(3, 1, 4, 1, 5, 9, 2, 6)
.sorted()
.build();
// Conditional elements
-List<String> filtered = listb(String.class)
+List<String> filtered = listBuilder(String.class)
.add("always")
.addIf(includeOptional, "optional")
.build();
// Immutable list
-List<String> immutable = listb(String.class)
+List<String> immutable = listBuilder(String.class)
.add("read", "only")
.unmodifiable()
.build();
@@ -46,19 +46,19 @@ Fluent builder for constructing `Map` instances.
import static org.apache.juneau.commons.utils.CollectionUtils.*;
// Basic usage
-Map<String, Integer> map = mapb(String.class, Integer.class)
+Map<String, Integer> map = mapBuilder(String.class, Integer.class)
.add("one", 1)
.add("two", 2)
.add("three", 3)
.build();
// Using pairs
-Map<String, String> props = mapb(String.class, String.class)
+Map<String, String> props = mapBuilder(String.class, String.class)
.addPairs("host", "localhost", "port", "8080")
.build();
// With sorting by key
-Map<String, Integer> sorted = mapb(String.class, Integer.class)
+Map<String, Integer> sorted = mapBuilder(String.class, Integer.class)
.add("zebra", 3)
.add("apple", 1)
.add("banana", 2)
@@ -73,12 +73,12 @@ Fluent builder for constructing `Set` instances with
automatic deduplication.
import static org.apache.juneau.commons.utils.CollectionUtils.*;
// Basic usage
-Set<String> set = setb(String.class)
+Set<String> set = setBuilder(String.class)
.add("apple", "banana", "cherry")
.build();
// Automatic deduplication
-Set<Integer> unique = setb(Integer.class)
+Set<Integer> unique = setBuilder(Integer.class)
.add(1, 2, 3, 2, 1) // Duplicates ignored
.build(); // Contains: 1, 2, 3
```
diff --git a/pages/topics/02.03.JuneauCommonsLang.md
b/pages/topics/02.03.JuneauCommonsLang.md
index f801ed4681..4108545fd3 100644
--- a/pages/topics/02.03.JuneauCommonsLang.md
+++ b/pages/topics/02.03.JuneauCommonsLang.md
@@ -101,32 +101,30 @@ if (name.isAny("John", "Jane", "Bob")) {
```
### <java-class><a
href="/site/apidocs/org/apache/juneau/commons/lang/StringFormat.html"
target="_blank">StringFormat</a></java-class>
-Unified string formatter supporting both MessageFormat-style and printf-style
formatting.
+Cacheable printf-style string formatter (backed by `String.format` semantics).
```java
-// Mixed format styles
-StringFormat fmt = StringFormat.of("Hello {0}, you have %d items");
+// Printf-style formatting
+StringFormat fmt = StringFormat.ofPrintf("Hello %s, you have %d items");
String result = fmt.format("John", 5);
// Returns: "Hello John, you have 5 items"
-// MessageFormat with explicit indices, printf with sequential
-StringFormat fmt2 = StringFormat.of("User {0} has %s and {1} items");
-String result2 = fmt2.format("Alice", 10, "admin");
-// Returns: "User Alice has admin and 10 items"
-// {0} -> "Alice", {1} -> 10, %s -> "admin"
-
-// Printf with explicit indices
-StringFormat fmt3 = StringFormat.of("%1$s loves %2$s, and {0} also loves
%3$s");
-String result3 = fmt3.format("Alice", "Bob", "Charlie");
+// Printf with explicit argument indices
+StringFormat fmt2 = StringFormat.ofPrintf("%1$s loves %2$s, and %1$s also
loves %3$s");
+String result2 = fmt2.format("Alice", "Bob", "Charlie");
// Returns: "Alice loves Bob, and Alice also loves Charlie"
+
+// A one-shot helper is also available for non-cached use
+String result3 = StringFormat.formatPrintf("%s has %d items", "Alice", 10);
+// Returns: "Alice has 10 items"
```
:::note
-The `StringFormat` engine itself still understands **both** grammars in a
single pattern (as shown above).
-However, as of 10.0.0 the terse formatting helpers `Shorts.f()` /
`Shorts.fs()` and `StringUtils.format(...)`
-are **printf-only** — they route the pattern straight through `String.format`
semantics, so `{0}`-style
-placeholders render literally. Use `Shorts.mf()` / `Shorts.mfs()` /
`StringUtils.mformat(...)` (or a direct
-`StringFormat.of(...)`) when you need MessageFormat-style `{0}` /
`''`-quoting. See the
+`StringFormat` is **printf-only** — MessageFormat-style `{0}` placeholders are
**not** recognized and render
+literally (`{` and `'` are treated as ordinary text). This applies
consistently across the terse helpers too:
+`Shorts.f()` / `Shorts.fs()` and `StringUtils.format(...)` are also
printf-only. If you need MessageFormat-style
+`{0}` / `''`-quoting, use `Shorts.mf()` / `Shorts.mfs()` /
`StringUtils.mformat(...)` (backed directly by
+`java.text.MessageFormat`) instead — there is no single class that mixes both
grammars in one pattern. See the
[10.0.0 release notes](/docs/release-notes/10.0.0) for migration details.
:::
@@ -146,9 +144,11 @@ Version management utility.
```java
Version version = Version.of("1.2.3");
-version.getMajor(); // 1
-version.getMinor(); // 2
-version.getPatch(); // 3
+version.getMajor(); // Optional.of(1)
+version.getMinor(); // Optional.of(2)
+version.getMaintenance(); // Optional.of(3)
+
+int major = version.getMajor().orElse(0); // 1
// Compare versions
Version v1 = Version.of("1.2.3");
@@ -157,11 +157,11 @@ int comparison = v1.compareTo(v2); // -1 (v1 < v2)
```
### <java-class><a
href="/site/apidocs/org/apache/juneau/commons/lang/VersionRange.html"
target="_blank">VersionRange</a></java-class>
-Version range utility for checking if a version falls within a range.
+Version range utility for checking if a version string falls within an
OSGi-style range.
```java
-VersionRange range = VersionRange.of("1.0.0", "2.0.0");
-boolean inRange = range.contains(Version.of("1.5.0")); // true
+VersionRange range = new VersionRange("[1.0.0,2.0.0)");
+boolean inRange = range.matches("1.5.0"); // true
```
### <java-enum><a
href="/site/apidocs/org/apache/juneau/commons/lang/StateEnum.html"
target="_blank">StateEnum</a></java-enum>
@@ -223,10 +223,10 @@ String found = result.orElse("not found");
### Version Checking
```java
-Version current = Version.of("1.2.3");
-VersionRange supported = VersionRange.of("1.0.0", "2.0.0");
+String current = "1.2.3";
+VersionRange supported = new VersionRange("[1.0.0,2.0.0)");
-if (supported.contains(current)) {
+if (supported.matches(current)) {
// Version is supported
}
```
diff --git a/pages/topics/02.06.JuneauCommonsSettings.md
b/pages/topics/02.06.JuneauCommonsSettings.md
index 95516dc3ca..a57868f05c 100644
--- a/pages/topics/02.06.JuneauCommonsSettings.md
+++ b/pages/topics/02.06.JuneauCommonsSettings.md
@@ -117,6 +117,26 @@ FunctionalPropertyStore store2 =
FunctionalPropertyStore.of(
);
```
+### Built-in Property Sources
+
+Beyond the generic `FunctionalPropertySource`/`MapStore` shown above,
`juneau-commons` ships several
+purpose-built `PropertySource` implementations, each with a `*Provider`
companion for
+`ServiceLoader`-driven auto-registration:
+
+- <java-class><a
href="/site/apidocs/org/apache/juneau/commons/settings/DotenvPropertySource.html"
target="_blank">DotenvPropertySource</a></java-class> — reads key-value pairs
from a `.env`-style file. Defaults to `./.env`; override the path via the
`juneau.dotenv.path` system property or `JUNEAU_DOTENV_PATH` environment
variable.
+- <java-class><a
href="/site/apidocs/org/apache/juneau/commons/settings/RelaxedPropertySource.html"
target="_blank">RelaxedPropertySource</a></java-class> — a decorator that
wraps a delegate source and retries a miss with canonical name variants
(upper-underscore and lower-dotted), so `MySection/myKey`, `my.key`, `myKey`,
and `MY_SECTION_MY_KEY` can all resolve the same logical property. The verbatim
name is always tried first.
+- <java-class><a
href="/site/apidocs/org/apache/juneau/commons/settings/ArgsPropertySource.html"
target="_blank">ArgsPropertySource</a></java-class> — backed by parsed
command-line `Args`, supporting lookups by flag name or positional index.
`ArgsPropertySource.createDefault()` derives the args from `sun.java.command`
(falling back to the `juneau.args` system property).
+- <java-class><a
href="/site/apidocs/org/apache/juneau/commons/settings/ManifestFilePropertySource.html"
target="_blank">ManifestFilePropertySource</a></java-class> — backed by a
classpath `META-INF/MANIFEST.MF` (via `ManifestFile`), memoized so the
classpath is scanned at most once per source instance.
`ManifestFilePropertySource.createDefault()` uses default classloader scanning.
+
+```java
+// Wrap the system-env source so env vars bind relaxedly (e.g. MY_PROP
satisfies my.prop).
+Settings settings = Settings.create()
+ .addSource(new RelaxedPropertySource(Settings.SYSTEM_ENV_SOURCE))
+ .addSource(new DotenvPropertySource())
+ .addSource(ArgsPropertySource.createDefault())
+ .build();
+```
+
### Spring Property Integration
`juneau-commons` itself has no Spring dependency — the `Settings` framework
only defines the pluggable `PropertySource`/`PropertyStore` SPI shown above;
the first-class Spring integration lives in the `juneau-rest-server-springboot`
module.
@@ -191,7 +211,7 @@ String appName = settings.get("app.name").get();
## System Properties
- `juneau.settings.disableGlobal` (system property) or
`JUNEAU_SETTINGS_DISABLEGLOBAL` (system env) - If set to `true`, prevents new
global overrides from being set via `setGlobal()`. Existing global overrides
will still be returned until explicitly removed.
-- `juneau.enableVerboseExceptions` - If set to `true`, every exception created
via the `ThrowableUtils` factory methods (`illegalArg`, `rex`, `bex`, `exex`,
etc.) immediately prints its stack trace to stderr at the point of creation.
This is useful during development and debugging to pinpoint exactly where an
exception originates, even if it is later caught and swallowed. See
[JuneauCommonsUtils](JuneauCommonsUtils) for details.
+- `juneau.enableVerboseExceptions` - Controls the **opt-in**
`ThrowableUtils.log(exception)` helper — it is never invoked automatically by
any exception factory (`iaex`, `brex`, `exex`, etc.). If set to `true`, calling
`log(exception)` logs the throwable's stack trace as a warning (not
automatically printed to stderr) and returns the same throwable unchanged, so
it can be used inline, e.g. `throw log(iaex("..."))`; when `false` (the
default), `log(...)` is a no-op passthrough. This is us [...]
## See Also
diff --git a/pages/topics/02.09.JuneauCommonsIO.md
b/pages/topics/02.09.JuneauCommonsIO.md
index bcdd67c230..4ef48e519f 100644
--- a/pages/topics/02.09.JuneauCommonsIO.md
+++ b/pages/topics/02.09.JuneauCommonsIO.md
@@ -47,9 +47,8 @@ Fluent builder for creating `Reader` instances from files.
```java
FileReaderBuilder builder = FileReaderBuilder.create()
- .file(Paths.get("file.txt"))
- .charset(StandardCharsets.UTF_8)
- .buffered(true);
+ .file("file.txt")
+ .charset(StandardCharsets.UTF_8);
Reader reader = builder.build();
```
@@ -59,10 +58,10 @@ Fluent builder for creating `Writer` instances for files.
```java
FileWriterBuilder builder = FileWriterBuilder.create()
- .file(Paths.get("output.txt"))
+ .file("output.txt")
.charset(StandardCharsets.UTF_8)
- .append(true)
- .buffered(true);
+ .append()
+ .buffered();
Writer writer = builder.build();
```
@@ -79,24 +78,25 @@ Reader reader = builder.build();
```
### <java-class><a
href="/site/apidocs/org/apache/juneau/commons/io/Console.html"
target="_blank">Console</a></java-class>
-Utility for logging formatted messages to the console.
+Utility for printing formatted (printf-style) messages to the console.
```java
-Console console = new Console();
-console.log("Hello {0}, you have {1} items", "John", 5);
+Console.out("Hello %s, you have %s items", "John", 5);
+Console.err("Something went wrong: %s", errorMessage);
```
### <java-class><a
href="/site/apidocs/org/apache/juneau/commons/io/MimeTypeDetector.html"
target="_blank">MimeTypeDetector</a></java-class>
-Utility for detecting MIME types from file extensions or content.
+Utility for detecting MIME types from file extensions.
```java
-MimeTypeDetector detector = new MimeTypeDetector();
-
-// Detect from file extension
-String mimeType = detector.detect("file.pdf"); // "application/pdf"
+// Use the default, pre-populated instance
+String mimeType = MimeTypeDetector.DEFAULT.getContentType("file.pdf"); //
"application/pdf"
-// Detect from path
-String mimeType2 = detector.detect(Paths.get("image.jpg")); // "image/jpeg"
+// Or build a customized instance
+MimeTypeDetector detector = MimeTypeDetector.builder()
+ .addDefaultMappings()
+ .build();
+String mimeType2 = detector.getContentType("image.jpg"); // "image/jpeg"
```
### <java-class><a
href="/site/apidocs/org/apache/juneau/commons/io/CharSequenceReader.html"
target="_blank">CharSequenceReader</a></java-class>
@@ -151,12 +151,12 @@ wrapped.close(); // Does not close the original writer
```java
// Try classpath first, then file system
LocalFile file = new LocalFile(MyClass.class, "config.properties");
-if (!file.exists()) {
- file = new LocalFile(Paths.get("/etc/myapp/config.properties"));
-}
-
-if (file.exists()) {
- try (InputStream is = file.read()) {
+try (InputStream is = file.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
}
}
@@ -191,19 +191,18 @@ LocalFile about = templates.resolve("about.html");
```java
// Read file with specific charset
try (Reader reader = FileReaderBuilder.create()
- .file(Paths.get("file.txt"))
+ .file("file.txt")
.charset(StandardCharsets.UTF_8)
- .buffered(true)
.build()) {
// Read from file
}
// Write file with append mode
try (Writer writer = FileWriterBuilder.create()
- .file(Paths.get("output.txt"))
+ .file("output.txt")
.charset(StandardCharsets.UTF_8)
- .append(true)
- .buffered(true)
+ .append()
+ .buffered()
.build()) {
writer.write("New content");
}
diff --git a/pages/topics/03.03.01.JavaBeansSupport.md
b/pages/topics/03.03.01.JavaBeansSupport.md
index b44be5414b..0166dc4675 100644
--- a/pages/topics/03.03.01.JavaBeansSupport.md
+++ b/pages/topics/03.03.01.JavaBeansSupport.md
@@ -31,21 +31,21 @@ public class MyBean {
public void setProperty5(String value) {...}
// Non-standard getters/setters identified by annotation.
- @MarshalledProp
+ @BeanProp
public String property6() {...}
- @MarshalledProp
+ @BeanProp
public void property6(String value) {...}
// Non-standard getters/setters identified by annotation with overridden
names.
- @MarshalledProp("property7")
+ @BeanProp("property7")
public String property7X() {...}
- @MarshalledProp("property7")
+ @BeanProp("property7")
public void property7X(String value) {...}
// Non-public getters/setters identified by annotation.
- @MarshalledProp
+ @BeanProp
private String getProperty8() {...}
- @MarshalledProp
+ @BeanProp
private void setProperty8(String value) {...}
// Ignore a method that looks like a getter.
diff --git a/pages/topics/03.03.02.JavaRecordsSupport.md
b/pages/topics/03.03.02.JavaRecordsSupport.md
index dba895b7bc..2a3c917d14 100644
--- a/pages/topics/03.03.02.JavaRecordsSupport.md
+++ b/pages/topics/03.03.02.JavaRecordsSupport.md
@@ -5,7 +5,7 @@ slug: JavaRecordsSupport
Juneau supports serialization and parsing of Java records out of the box.
Records are treated as beans whose
properties are derived from the record's components. The canonical
constructor is automatically detected for
-parsing, so no `@MarshalledCtor` annotation is needed.
+parsing, so no `@BeanCtor` annotation is needed.
## Basic Usage
@@ -47,33 +47,33 @@ Records work with the full range of Juneau's bean
annotations and capabilities:
public record AnnotatedOrder(String name, int age) {}
```
-### Property renaming with `@MarshalledProp`
+### Property renaming with `@BeanProp`
```java
-public record WithMarshalledProp(@MarshalledProp(name="fullName") String name,
int age) {}
+public record WithBeanProp(@BeanProp(name="fullName") String name, int age) {}
```
```java
-String json = Json5Serializer.DEFAULT.write(new WithMarshalledProp("John",
30));
+String json = Json5Serializer.DEFAULT.write(new WithBeanProp("John", 30));
// Produces: {age:30,fullName:'John'}
```
-### Non-canonical constructors with `@MarshalledCtor`
+### Non-canonical constructors with `@BeanCtor`
-For records, the canonical constructor is automatically detected, so
`@MarshalledCtor` is only needed if you want to use
+For records, the canonical constructor is automatically detected, so
`@BeanCtor` is only needed if you want to use
a non-canonical constructor, for example to provide default values for certain
components:
```java
@BeanType(properties="name")
-public record WithMarshalledCtor(String name, int age) {
- @MarshalledCtor(properties="name")
- public WithMarshalledCtor(String name) {
+public record WithBeanCtor(String name, int age) {
+ @BeanCtor(properties="name")
+ public WithBeanCtor(String name) {
this(name, 0); // Default age to 0
}
}
```
-When using `@MarshalledCtor` on a non-canonical constructor, use
`@BeanType(properties)` to limit the visible properties to
+When using `@BeanCtor` on a non-canonical constructor, use
`@BeanType(properties)` to limit the visible properties to
match the constructor parameters.
### Compact constructors
@@ -145,8 +145,8 @@ operations are **not supported during parsing**:
- **`@MarshalledIgnore` on individual record components** -- Ignoring a
component will exclude it from serialization
output, but the parser will be unable to instantiate the record if the
component value is missing from the input.
- **`@BeanType(excludeProperties)` excluding record components** -- Same
limitation as `@MarshalledIgnore`.
-- **`@BeanType(readOnlyProperties)` / `@MarshalledProp(ro)` on record
components** -- Marking components as read-only will
+- **`@BeanType(readOnlyProperties)` / `@BeanProp(ro)` on record components**
-- Marking components as read-only will
prevent them from being parsed, making the record un-parseable if the values
are missing from the input.
These annotations will still affect serialization as expected. If you need to
omit components during parsing,
-use `@MarshalledCtor` with a non-canonical constructor that provides defaults
for the omitted components.
+use `@BeanCtor` with a non-canonical constructor that provides defaults for
the omitted components.
diff --git a/pages/topics/03.03.06.BeanIgnoreAnnotation.md
b/pages/topics/03.03.06.BeanIgnoreAnnotation.md
index fabcb625fb..c804b6975b 100644
--- a/pages/topics/03.03.06.BeanIgnoreAnnotation.md
+++ b/pages/topics/03.03.06.BeanIgnoreAnnotation.md
@@ -105,7 +105,7 @@ public class MyBean {
}
```
-The logical property name is derived from the field the same way as for
visible fields: `@MarshalledProp` /
+The logical property name is derived from the field the same way as for
visible fields: `@BeanProp` /
`@Name` on the field, if present, otherwise the marshalling context's
`PropertyNamer` applied to the field name.
That name must match the accessor-derived property name (e.g. field `foo` with
`getFoo`/`setFoo`).
diff --git a/pages/topics/03.13.00.Swaps.md b/pages/topics/03.13.00.Swaps.md
index 63c3fe1c56..ab483f5787 100644
--- a/pages/topics/03.13.00.Swaps.md
+++ b/pages/topics/03.13.00.Swaps.md
@@ -17,7 +17,7 @@ containing customized key-value pairs:
```java
// Sample swap for converting a bean to a specialized map of key-value pairs.
-public class MyBeanSwap extends ObjectSwap {
+public class MyBeanSwap extends ObjectSwap<MyBean,JsonMap> {
// Converts a bean to a generic map.
@Override /* ObjectSwap */
@@ -27,7 +27,7 @@ public class MyBeanSwap extends ObjectSwap {
// Converts the generic map back into a bean.
@Override /* ObjectSwap */
- public MyBean unswap(MarshallingSession session, JsonMap map, ClassMeta
hint) throws Exception {
+ public MyBean unswap(MarshallingSession session, JsonMap map, ClassMeta<?>
hint) throws Exception {
MyBean bean = new MyBean();
bean.setBar(map.getString("foo"));
return bean;
@@ -53,7 +53,7 @@ Another example of a `ObjectSwap` is one that converts
`byte[]` arrays to BASE64
public class ByteArrayBase64Swap extends StringSwap {
@Override /* StringSwap */
- public String swap(byte[] bytes) throws Exception {
+ public String swap(MarshallingSession session, byte[] bytes) throws
Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStream b64os = MimeUtility.encode(baos, "base64");
b64os.write(bytes);
@@ -62,7 +62,7 @@ public class ByteArrayBase64Swap extends StringSwap {
}
@Override /* StringSwap */
- public byte[] unswap(String string, ClassMeta hint) throws Exception {
+ public byte[] unswap(MarshallingSession session, String string,
ClassMeta<?> hint) throws Exception {
byte[] bytes = string.getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
InputStream b64is = MimeUtility.decode(bais, "base64");
diff --git a/pages/topics/03.13.03.PerMediaTypeSwaps.md
b/pages/topics/03.13.03.PerMediaTypeSwaps.md
index 13582a2c15..ca26b34baa 100644
--- a/pages/topics/03.13.03.PerMediaTypeSwaps.md
+++ b/pages/topics/03.13.03.PerMediaTypeSwaps.md
@@ -64,13 +64,13 @@ public class ObjectSwapTest {
MyPojo myPojo = new MyPojo();
- String json =
serializers.getWriterSerializer("text/json").write(myPojo);
+ String json =
serializers.getWriterSerializer("text/json").orElseThrow().write(myPojo);
assertEquals("'It\\'s JSON!'", json);
- String xml = serializers.getWriterSerializer("text/xml").write(myPojo);
+ String xml =
serializers.getWriterSerializer("text/xml").orElseThrow().write(myPojo);
assertEquals("It's XML!", xml);
- String html =
serializers.getWriterSerializer("text/html").write(myPojo);
+ String html =
serializers.getWriterSerializer("text/html").orElseThrow().write(myPojo);
assertEquals("It's something else!", html);
}
}
diff --git a/pages/topics/03.39.00.JsonlSupport.md
b/pages/topics/03.39.00.JsonlSupport.md
index 37ca9ce182..4072b124d7 100644
--- a/pages/topics/03.39.00.JsonlSupport.md
+++ b/pages/topics/03.39.00.JsonlSupport.md
@@ -80,11 +80,7 @@ The conversion follows the <a href="https://jsonlines.org/"
target="_blank">JSON
## REST Integration
```java
-// Use BasicUniversalConfig for JSONL-only REST resources
-@Rest
-public class MyResource extends RestServlet implements BasicUniversalConfig {
... }
-
-// JSONL is also included in BasicUniversalConfig
+// JSONL support is included via BasicUniversalConfig (there is no dedicated
JSONL-only config interface).
@Rest
public class MyResource extends RestServlet implements BasicUniversalConfig {
... }
```
diff --git a/pages/topics/03.42.Bson.md b/pages/topics/03.42.Bson.md
index ab099a9eec..dd5c4912ff 100644
--- a/pages/topics/03.42.Bson.md
+++ b/pages/topics/03.42.Bson.md
@@ -82,11 +82,7 @@ byte[] bson = s.write(someObject);
## REST Integration
```java
-// Use BasicUniversalConfig for BSON-only REST resources
-@Rest
-public class MyResource extends RestServlet implements BasicUniversalConfig {
... }
-
-// BSON is also included in BasicUniversalConfig
+// BSON support is included via BasicUniversalConfig (there is no dedicated
BSON-only config interface).
@Rest
public class MyResource extends RestServlet implements BasicUniversalConfig {
... }
diff --git a/pages/topics/03.44.HoconSupport.md
b/pages/topics/03.44.HoconSupport.md
index 5406961ee1..d17149c18c 100644
--- a/pages/topics/03.44.HoconSupport.md
+++ b/pages/topics/03.44.HoconSupport.md
@@ -93,13 +93,9 @@ String compact = HoconSerializer.DEFAULT_COMPACT.write(bean);
## REST Integration
```java
-// HOCON is included in BasicUniversalConfig
+// HOCON support is included via BasicUniversalConfig (there is no dedicated
HOCON-only config interface).
@Rest
public class MyResource extends RestServlet implements BasicUniversalConfig {
... }
-
-// HOCON-only resource
-@Rest
-public class HoconResource extends RestServlet implements BasicUniversalConfig
{ ... }
```
## Round-Trip Notes
diff --git a/pages/topics/03.47.06.LargeDatasetStreaming.md
b/pages/topics/03.47.06.LargeDatasetStreaming.md
index 3c7ca1600c..b071122bc1 100644
--- a/pages/topics/03.47.06.LargeDatasetStreaming.md
+++ b/pages/topics/03.47.06.LargeDatasetStreaming.md
@@ -173,15 +173,15 @@ public class EmployeeConsumer implements
BeanConsumer<Employee> {
}
```
-Parsing via `parseToBeanConsumer`:
+Parsing via `readToBeanConsumer`:
```java
// Direct API — framework calls begin(), acceptThrows() per element,
complete().
var consumer = consumerFactory.create();
-JsonParser.DEFAULT.getSession().parseToBeanConsumer(inputStream, consumer,
Employee.class);
+JsonParser.DEFAULT.getSession().readToBeanConsumer(inputStream, consumer,
Employee.class);
```
-In a Spring REST resource:
+In a REST resource:
```java
@Rest
@@ -193,7 +193,8 @@ public class EmployeeResource extends BasicRestServlet {
@RestPost("/employees/bulk")
public void importEmployees(RestRequest req) throws Exception {
var consumer = consumerFactory.create();
- req.getBody().parseToBeanConsumer(consumer, Employee.class);
+ var parser =
req.getContent().getParserMatch().orElseThrow().getParser();
+
parser.getSession().readToBeanConsumer(req.getContent().getInputStream(),
consumer, Employee.class);
}
}
```
diff --git a/pages/topics/05.04.JuneauBeanOpenApi3.md
b/pages/topics/05.04.JuneauBeanOpenApi3.md
index a80c7b9ed0..53e0221edd 100644
--- a/pages/topics/05.04.JuneauBeanOpenApi3.md
+++ b/pages/topics/05.04.JuneauBeanOpenApi3.md
@@ -167,22 +167,16 @@ public class OpenApiUI extends ObjectSwap {
## Usage in REST Services
-The <a
href="/site/apidocs/org/apache/juneau/rest/server/servlet/BasicRestServlet.html"
target="_blank">BasicRestServlet</a> class shows how this swap is used in the
REST interface to generate the OpenAPI UI:
+The <a
href="/site/apidocs/org/apache/juneau/rest/server/servlet/BasicRestServlet.html"
target="_blank">BasicRestServlet</a> class exposes the OpenAPI document for a
resource through a built-in `OPTIONS /*` operation; content negotiation (e.g.
requesting `text/html`) determines whether the `OpenApiUI` swap is applied when
rendering it:
```java
@Rest(
// Allow OPTIONS requests to be simulated using ?method=OPTIONS query
parameter.
allowedMethodParams="OPTIONS",
+ mixins={SwaggerUiMixin.class, RedocMixin.class, ErrorMixin.class,
HtdocMixin.class, StatsMixin.class, FaviconMixin.class},
...
)
-@MarshalledConfig(
- // POJO swaps to apply to all serializers/parsers.
- swaps={
- // Use the OpenApiUI swap when rendering OpenAPI beans.
- OpenApiUI.class
- }
-)
-public abstract class BasicRestServlet extends RestServlet implements
BasicRestConfig {
+public abstract class BasicRestServlet extends RestServlet implements
BasicUniversalConfig {
/**
* [OPTIONS /*] - Show resource options.
@@ -204,7 +198,7 @@ public abstract class BasicRestServlet extends RestServlet
implements BasicRestC
)
public OpenApi getOptions(RestRequest req) {
// Localized OpenAPI for this resource is available through the
RestRequest object.
- return req.getOpenApi();
+ return req.getOpenApi().orElseThrow();
}
}
```
diff --git a/pages/topics/07.02.01.CustomErrorMessages.md
b/pages/topics/07.02.01.CustomErrorMessages.md
index b43d459e23..10b1b8dc3f 100644
--- a/pages/topics/07.02.01.CustomErrorMessages.md
+++ b/pages/topics/07.02.01.CustomErrorMessages.md
@@ -202,7 +202,7 @@ void setUp() {
.addStringifier(LocalDate.class, date ->
date.format(DateTimeFormatter.ISO_LOCAL_DATE))
.build();
- BctAssertions.setConverter(converter);
+ BctConfiguration.set(converter);
}
// Use custom message with the converter
@@ -211,7 +211,7 @@ assertBean(fs("Date validation failed for user %s", userId),
@AfterEach
void tearDown() {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
diff --git a/pages/topics/07.02.02.01.Stringifiers.md
b/pages/topics/07.02.02.01.Stringifiers.md
index 50dc873870..87f6973934 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
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertBean(order, "total,customer", "$99.99,{John Smith
<joh***@example.com>}");
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
@@ -155,11 +155,11 @@ var converter = BasicBeanConverter.builder()
.build();
// Use in assertions
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertBean(order, "date,total", "2023-12-01,99.99");
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
diff --git a/pages/topics/07.02.02.02.Listifiers.md
b/pages/topics/07.02.02.02.Listifiers.md
index b66811f31e..81cfea53d9 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);
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertList(page, "Alice", "Bob", "Charlie");
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
// Test database results
ResultSet rs = statement.executeQuery("SELECT name FROM users");
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertList(rs,
predicate(row -> ((Map)row).get("name").equals("Alice")),
predicate(row -> ((Map)row).get("name").equals("Bob")));
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
@@ -169,13 +169,13 @@ try {
```java
// Test collection properties
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertBean(paginatedResult,
"items{#{name}},totalCount",
"[{Alice},{Bob},{Charlie}],3");
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
diff --git a/pages/topics/07.02.02.03.Swappers.md
b/pages/topics/07.02.02.03.Swappers.md
index 040c457723..1c9910aed8 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);
-BctAssertions.setConverter(converter);
+BctConfiguration.set(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 {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
@@ -199,11 +199,11 @@ ValidationResult<Order> invalidValidation =
orderValidator.validate(invalidOrder
```java
// Test lazy computation
LazyValue<Report> lazyReport = new LazyValue<>(() -> generateReport());
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertBean(lazyReport, "title,itemCount", "Monthly Report,150");
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
// 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 e75d793f52..30b1d98ca3 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
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertBean(user, "fname,lname,email", "John,Doe,[email protected]");
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
@@ -151,12 +151,12 @@ PropertyExtractor computedExtractor = new
PropertyExtractor() {
};
// Usage
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertBean(user, "computed_fullName,computed_age,computed_initials",
"John Doe,30,J.D.");
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
@@ -210,11 +210,11 @@ PropertyExtractor privateFieldExtractor = new
PropertyExtractor() {
};
// Usage
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertBean(myBean, "_privateField1,_privateField2", "value1,value2");
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
@@ -276,12 +276,12 @@ PropertyExtractor configExtractor = new
PropertyExtractor() {
};
// Usage
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertBean(config, "database.host,database.port,app.name",
"localhost,5432,MyApp");
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
@@ -447,7 +447,7 @@ PropertyExtractor fallbackExtractor = new
PropertyExtractor() {
```java
// Test database entity
DatabaseEntity entity = loadEntity(123);
-BctAssertions.setConverter(converter);
+BctConfiguration.set(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 {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
@@ -468,12 +468,12 @@ User user = loadUser(456);
```java
// Use property extractor with nested access
-BctAssertions.setConverter(converter);
+BctConfiguration.set(converter);
try {
assertBean(order, "customer{computed_fullName},items{0{name}}",
"{John Doe},{{Laptop}}");
} finally {
- BctAssertions.resetConverter();
+ BctConfiguration.clear();
}
```
diff --git a/pages/topics/10.05.Marshalling.md
b/pages/topics/10.05.Marshalling.md
index 610405f6d1..272d83149f 100644
--- a/pages/topics/10.05.Marshalling.md
+++ b/pages/topics/10.05.Marshalling.md
@@ -16,10 +16,10 @@ Serializers and parsers can be associated with REST
servlets using the following
<tree>
<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Rest.html"
target="_blank">Rest</a></java-annotation></node-0>
<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Rest.html#serializers()"
target="_blank">serializers</a></java-method-annotation></node-1>
-<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Rest.html#serializers()"
target="_blank">parsers</a></java-method-annotation></node-1>
+<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Rest.html#parsers()"
target="_blank">parsers</a></java-method-annotation></node-1>
<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/RestOp.html"
target="_blank">RestOp</a></java-annotation></node-0>
<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/RestOp.html#serializers()"
target="_blank">serializers</a></java-method-annotation></node-1>
-<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/RestOp.html#serializers()"
target="_blank">parsers</a></java-method-annotation></node-1>
+<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/RestOp.html#parsers()"
target="_blank">parsers</a></java-method-annotation></node-1>
</tree>
Request bodies are parsed and passed in via <a
href="/site/apidocs/org/apache/juneau/http/Content.html"
target="_blank">@Content</a>-annotated parameters, and response bodies are
returned or thrown by <a
href="/site/apidocs/org/apache/juneau/rest/server/RestOp.html"
target="_blank">@RestOp</a>-annotated methods and serialized.
@@ -27,7 +27,7 @@ Request bodies are parsed and passed in via <a
href="/site/apidocs/org/apache/ju
:::tip Example
```java
@Rest(
- serializers={JsonParser.class, HtmlSerializer.class},
+ serializers={JsonSerializer.class, HtmlSerializer.class},
parsers={JsonParser.class, HtmlParser.class}
)
public class MyResource {
diff --git a/pages/topics/10.20.StaticFiles.md
b/pages/topics/10.20.StaticFiles.md
index e0b4d228f7..dee26eb29d 100644
--- a/pages/topics/10.20.StaticFiles.md
+++ b/pages/topics/10.20.StaticFiles.md
@@ -36,6 +36,17 @@ StaticFiles
.headers(cacheControl("max-age=86400, public")); // Add cache control.
```
+:::caution Mount collision with `StaticFilesMixin`
+`BasicRestServlet`/`BasicRestResource` already bake in `HtdocMixin`, which
serves `/htdocs/*` by
+default. The separate, opt-in
[`StaticFilesMixin`](/docs/topics/StaticFilesMixin) *also*
+default-mounts `/htdocs/*` (plus `/static/*`). Because `HtdocMixin` and
`StaticFilesMixin` are
+different classes, the mixin walk's dedupe-by-class-identity rule does not
collapse them — adding
+`StaticFilesMixin` to a `BasicRestServlet` subclass registers two mixins
competing for the same
+`/htdocs/*` mount. If you need `StaticFilesMixin`'s multi-mount/OSGi-friendly
packaging on a
+`BasicRestServlet` subclass, test the combined behavior explicitly, or build
on plain `RestServlet`
+instead so only one static-file mixin is in play.
+:::
+
Static files can be configured programmatically through the following APIs:
<tree>
diff --git a/pages/topics/10.21.StaticFilesMixin.md
b/pages/topics/10.21.StaticFilesMixin.md
index 05d2393b1f..ffd14603e0 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.getStaticFiles().resolve(path,
locale).orElseThrow(NotFound::new);
+ return req.getContext().getStaticFiles().resolve(path,
locale).orElseThrow(NotFound::new);
}
@RestOp(
@@ -54,6 +54,17 @@ the [problem-details
opt-in](/docs/topics/RestServerProblemDetails) is active; p
## Default mount usage
+:::caution Mount collision on `BasicRestServlet` subclasses
+`BasicRestServlet`/`BasicRestResource` already bake in
+[`HtdocMixin`](/site/apidocs/org/apache/juneau/rest/server/ops/HtdocMixin.html),
which serves
+`/htdocs/*` by default (see [Static files](/docs/topics/StaticFiles)). Adding
`StaticFilesMixin`
+to a `BasicRestServlet` subclass registers a *second*,
independently-implemented mixin that also
+default-mounts `/htdocs/*` — `HtdocMixin` and `StaticFilesMixin` are different
classes, so the
+mixin walk's dedupe-by-class-identity rule does not collapse them. This page's
examples build on
+plain `RestServlet` (no baked-in `HtdocMixin`) specifically to avoid that
overlap; if you add
+`StaticFilesMixin` to a `BasicRestServlet` subclass instead, test the combined
behavior explicitly.
+:::
+
Add the mixin to any `@Rest`-annotated resource — no subclassing of
`BasicRestServlet`
required — and `/static/*` and `/htdocs/*` start serving classpath resources
immediately:
diff --git a/pages/topics/10.22.ConventionEndpointsMixins.md
b/pages/topics/10.22.ConventionEndpointsMixins.md
index 05c5034c8d..4cbc2d4b33 100644
--- a/pages/topics/10.22.ConventionEndpointsMixins.md
+++ b/pages/topics/10.22.ConventionEndpointsMixins.md
@@ -28,8 +28,14 @@ are not API-meaningful, and shipping them in the spec just
clutters the contract
## Composing the pack
-Each mixin is independent — drop in only the ones you need. The plan-A
composition mounts all
-four with builder-driven configuration:
+Each mixin is independent — drop in only the ones you need. The examples below
build on plain
+`RestServlet`. If you're building on `BasicRestServlet`/`BasicRestResource`
instead, note that
+`FaviconMixin` (along with `ErrorMixin`, `HtdocMixin`, and `StatsMixin`) is
already baked in by
+default — re-declaring `mixins=FaviconMixin.class` on the subclass is
redundant but harmless, since
+the mixin walk collects mixins in parent-to-child order and dedupes by class
identity (see
+[Mixin Sub-Contexts](/docs/topics/RestServerMixinSubContexts) for the walk
details).
+
+The plan-A composition mounts all four with builder-driven configuration:
```java
@Rest(
diff --git a/pages/topics/10.28.ClientVersioning.md
b/pages/topics/10.28.ClientVersioning.md
index c53de5e503..b997a736bb 100644
--- a/pages/topics/10.28.ClientVersioning.md
+++ b/pages/topics/10.28.ClientVersioning.md
@@ -39,5 +39,6 @@ public class MyResource {
public Object method3() {
...
}
+}
```
:::
\ No newline at end of file
diff --git a/pages/topics/10.32.RestServerLoggingAndDebugging.md
b/pages/topics/10.32.RestServerLoggingAndDebugging.md
index 650f1223ce..d552d3aca6 100644
--- a/pages/topics/10.32.RestServerLoggingAndDebugging.md
+++ b/pages/topics/10.32.RestServerLoggingAndDebugging.md
@@ -8,57 +8,76 @@ The REST APIs provides supports enabling logging of HTTP
requests and responses
<tree>
<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Rest.html"
target="_blank">Rest</a></java-annotation></node-0>
<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Rest.html#debug()"
target="_blank">debug</a></java-method-annotation></node-1>
-<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Rest.html#debugOn()"
target="_blank">debugOn</a></java-method-annotation></node-1>
<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/RestOp.html"
target="_blank">RestOp</a></java-annotation></node-0>
<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/RestOp.html#debug()"
target="_blank">debug</a></java-method-annotation></node-1>
+<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Debug.html"
target="_blank">Debug</a></java-annotation></node-0>
+<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Debug.html#value()"
target="_blank">value</a></java-method-annotation></node-1>
+<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Debug.html#format()"
target="_blank">format</a></java-method-annotation></node-1>
+<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Debug.html#level()"
target="_blank">level</a></java-method-annotation></node-1>
+<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Debug.html#on()"
target="_blank">on</a></java-method-annotation></node-1>
+<node-1><java-method-annotation><a
href="/site/apidocs/org/apache/juneau/rest/server/Debug.html#config()"
target="_blank">config</a></java-method-annotation></node-1>
</tree>
+Both <a href="/site/apidocs/org/apache/juneau/rest/server/Rest.html#debug()"
target="_blank">@Rest(debug)</a> and
+<a href="/site/apidocs/org/apache/juneau/rest/server/RestOp.html#debug()"
target="_blank">@RestOp(debug)</a> take a nested
+<a href="/site/apidocs/org/apache/juneau/rest/server/Debug.html"
target="_blank">@Debug</a> annotation rather than a plain string.
+
Debug mode enables the following:
- HTTP request/response bodies are cached in memory for logging purposes.
- HTTP requests/responses are logged to the registered <a
href="/site/apidocs/org/apache/juneau/rest/server/logger/CallLogger.html"
target="_blank">CallLogger</a>.
-The possible annotation values are:
+The possible <a
href="/site/apidocs/org/apache/juneau/rest/server/Debug.html#value()"
target="_blank">@Debug</a> values are:
-- `true` - Debug is enabled for all requests.
-- `false` - Debug is disabled for all requests.
+- `always` (or `true`) - Debug is enabled for all requests.
+- `never` (or `false`) - Debug is disabled for all requests.
- `conditional` - Debug is enabled only for requests that have a `Debug: true`
header.
+- `""` (the default) - Inherit the enablement from the enclosing scope.
:::tip Example
```java
// Enable debugging on all op calls on this resource
-@Rest(debug="true")
+@Rest(debug=@Debug("always"))
public class MyResource {...}
```
:::
-These annotations support SVL variables, so it's possible to define them as a
system property for example.
+`@Debug`'s `value()` supports SVL variables, so it's possible to define it as
a system property for example.
```java
// Enable via system property 'MyResource.debug'
-@Rest(debug="$S{MyResource.debug}")
+@Rest(debug=@Debug("$S{MyResource.debug}"))
public class MyResource {...}
```
-The <a href="/site/apidocs/org/apache/juneau/rest/server/Rest.html#debugOn()"
target="_blank">@Rest(debugOn)</a> annotation can also be used to
-enable debugging.
-It takes a comma-delimited list of key-value pairs, the keys being class or
method names, and the values being one of `true|false|conditional`.
+The <a href="/site/apidocs/org/apache/juneau/rest/server/Debug.html#on()"
target="_blank">@Debug(on)</a> attribute can also be used to
+enable debugging on specific classes or methods regardless of their own
`@Rest`/`@RestOp` debug setting.
+It takes a comma-delimited list of key-value pairs, the keys being
fully-qualified class or method names, and the values being one of
`always|never|conditional` (also resolved via SVL variables):
```java
// Turn on debug per-request on the class and always on the doX() method
-@Rest(debugOn="MyResource=conditional,MyResource.doX=true")
+@Rest(debug=@Debug(on="com.foo.MyResource=conditional,com.foo.MyResource.doX=always"))
public class MyResource {...}
```
-The primary advantage of <a
href="/site/apidocs/org/apache/juneau/rest/server/Rest.html#debugOn()"
target="_blank">@Rest(debugOn)</a> is that
+The primary advantage of <a
href="/site/apidocs/org/apache/juneau/rest/server/Debug.html#on()"
target="_blank">@Debug(on)</a> is that
you can control debugging externally such as through a system property or
environment variable:
```java
// Turn on debug per-request on the class and always on the doX() method
-@Rest(debugOn="$E{DEBUG}")
+@Rest(debug=@Debug(on="$E{DEBUG}"))
public class MyResource {...}
```
+:::note
+`@Debug` also supports a JUL <a
href="/site/apidocs/org/apache/juneau/rest/server/Debug.html#level()"
target="_blank">level()</a>
+attribute (parsed via `java.util.logging.Level.parse(String)`) for controlling
the log level debug entries are emitted at,
+and a <a
href="/site/apidocs/org/apache/juneau/rest/server/Debug.html#config()"
target="_blank">config()</a> attribute pointing at a
+<a href="/site/apidocs/org/apache/juneau/rest/server/debug/DebugConfig.html"
target="_blank">DebugConfig</a> subclass for full
+class/method-targeted rule programming (custom conditional predicates,
per-target formats, body caching, etc.) beyond what
+`value()`/`on()` alone can express.
+:::
+
Debugging can also be enabled programmatically with the use of the following
APIs:
<tree>
@@ -137,20 +156,19 @@ It can be changed through any of the following:
The call logger uses logging rules to map requests to logging detail levels.
-By default, these are the logging rules:
+By default, these are the logging rules (from <a
href="/site/apidocs/org/apache/juneau/rest/server/logger/BasicCallLogger.html"
target="_blank">BasicCallLogger</a>):
```java
-RestLogger
- .create()
- .beanStore(beanStore) // Allow injected beans in constructor
+CallLogger
+ .create(beanStore)
.normalRules( // Rules when debugging is not enabled
- RestLoggerRule.create() // Log 500+ errors with status-line and
header information
+ CallLoggerRule.create(beanStore) // Log 500+ errors with status-line
and header information
.statusFilter(x -> x >= 500)
.level(SEVERE)
.requestDetail(HEADER)
.responseDetail(HEADER)
.build(),
- RestLoggerRule.create() // Log 400-500 errors with just status-line
information
+ CallLoggerRule.create(beanStore) // Log 400-500 errors with just
status-line information
.statusFilter(x -> x >= 400)
.level(WARNING)
.requestDetail(STATUS_LINE)
@@ -158,14 +176,25 @@ RestLogger
.build()
)
.debugRules( // Rules when debugging is enabled
- RestLoggerRule.create() // Log everything with full details
+ CallLoggerRule.create(beanStore) // Log everything with full details
.level(SEVERE)
.requestDetail(ENTITY)
.responseDetail(ENTITY)
.build()
- );
+ )
+ .build();
```
+:::note Credential header redaction
+Regardless of the configured <a
href="/site/apidocs/org/apache/juneau/rest/server/logger/CallLoggingDetail.html"
target="_blank">CallLoggingDetail</a>,
+<a href="/site/apidocs/org/apache/juneau/rest/server/logger/CallLogger.html"
target="_blank">CallLogger</a> masks a
+built-in set of well-known credential-bearing headers (`Authorization`,
`Cookie`, `X-API-Key`, etc.) whenever it logs
+headers at `HEADER`/`ENTITY` detail, so caller-supplied secrets never end up
in cleartext in the log just because a
+request tripped a 5xx. Replace the redacted-header set via
`Builder.redactedHeaders(String...)` (pass an empty array
+to disable redaction and restore raw headers — not recommended outside a
trusted environment) or extend it via
+`Builder.redactHeader(String)`.
+:::
+
Thrown exceptions get logged with a stack trace hash and a counter like below:
```text
diff --git a/pages/topics/10.38.RestServerAuthGuards.md
b/pages/topics/10.38.RestServerAuthGuards.md
index 410cdfdeab..db905c786a 100644
--- a/pages/topics/10.38.RestServerAuthGuards.md
+++ b/pages/topics/10.38.RestServerAuthGuards.md
@@ -152,22 +152,24 @@ var guard =
BearerTokenGuard.create().realm("api").validator(validator).build();
- **JWKS rotation** — keys are re-fetched after the configured TTL (default 5
minutes). On JWKS fetch failure the cache continues serving the last-known-good
key set with a `WARNING`-level log entry, avoiding correlated auth outages from
transient network blips.
- **JWKS eager refresh on `kid` miss** — when a fresh-cache key selection
returns no matching keys (typically because an IdP rotated its signing key
mid-TTL), the cache performs one out-of-band JWKS refresh before failing the
request. Bounded by a 10-second cooldown and a single-in-flight guard. Enabled
by default; opt out with `jwksEagerRefreshOnKidMiss(false)`. Applies only to
`jwksUrl(...)`-backed caches — a caller-supplied `jwkSource(...)` is unaffected.
-## Composing with `BasicAdminResource`
+## Composing with `AdminMixin`
-`BasicAdminResource` ships with `DenyAllGuard` as its default — the secure
default is "no admin access until you wire something up." Swapping in
`BearerTokenGuard` (or any other guard) is a zero-mixin-source-change migration:
+[`AdminMixin`](/site/apidocs/org/apache/juneau/rest/server/ops/AdminMixin.html)
ships with `DenyAllGuard` as its default — the secure default is "no admin
access until you wire something up." Swapping in `BearerTokenGuard` (or any
other guard) is a zero-mixin-source-change migration:
```java
-@Rest(path="/myapp")
-public class MyAppResource extends BasicRestServlet implements
BasicAdminResource {
+@Rest(path="/myapp", mixins=AdminMixin.class)
+public class MyAppResource extends BasicRestServlet {
- @Bean // overrides the
mixin's DenyAllGuard.
- public RestGuardList adminGuards(BeanStore bs) {
- return RestGuardList.of(new BearerTokenGuard(myTokenValidator));
+ @Bean(name="guards") // overrides the
mixin's DenyAllGuard.
+ public RestGuardList guards(BeanStore bs) {
+ return RestGuardList.create(bs)
+ .append(new BearerTokenGuard(myTokenValidator))
+ .build();
}
}
```
-The mixin honors whatever guard the host registers; nothing inside
`BasicAdminResource` needs to change.
+The mixin honors whatever guard the host registers via the bean-store override
seam; nothing inside `AdminMixin` needs to change. (A routed-child flavor,
[`AdminResource`](/site/apidocs/org/apache/juneau/rest/server/ops/AdminResource.html),
and a standalone-servlet flavor, `AdminServlet`, are also available and follow
the same default-deny-plus-`@Bean`-override pattern.)
## Composing with rate-limit guards
diff --git a/pages/topics/13.16.StreamingCursors.md
b/pages/topics/13.16.StreamingCursors.md
index 2117df98af..1d5126bc13 100644
--- a/pages/topics/13.16.StreamingCursors.md
+++ b/pages/topics/13.16.StreamingCursors.md
@@ -33,7 +33,7 @@ try (var resp = client.get("/api/records").run();
// Stream is open; pass it to the cursor factory
Marshaller m = Json.DEFAULT;
- try (RecordReader r = m.fromRecords(in)) {
+ try (RecordReader r = m.readRecords(in)) {
while (r.canRead()) {
MyBean bean = r.read(MyBean.class);
process(bean);
@@ -48,9 +48,9 @@ or any other buffering method before opening the cursor —
those consume the st
---
-## `fromRecords()` — whole-value record cursor
+## `readRecords()` — whole-value record cursor
-`Marshaller.fromRecords(InputStream)` opens a
+`Marshaller.readRecords(Object)` opens a
<a href="/site/apidocs/org/apache/juneau/marshall/stream/RecordReader.html"
target="_blank">RecordReader</a>
that yields one deserialized value per call. It works with every format that
implements
`RecordReadable`.
@@ -62,7 +62,7 @@ try (var resp = client.get("/api/export")
.header("Accept", "application/jsonl")
.run();
InputStream in = resp.body().asStream();
- RecordReader r = m.fromRecords(in)) {
+ RecordReader r = m.readRecords(in)) {
while (r.canRead()) {
MyBean bean = r.read(MyBean.class);
@@ -75,10 +75,10 @@ Stream and cursor can be opened together in a single `try`
resource list.
---
-## `fromArrayRecords()` — element-by-element array streaming
+## `readArrayRecords()` — element-by-element array streaming
When the server returns a single top-level JSON (or CBOR / MsgPack) array,
-`fromArrayRecords()` streams each element as a separate record without
buffering the
+`readArrayRecords()` streams each element as a separate record without
buffering the
full array:
```java
@@ -86,7 +86,7 @@ Marshaller m = Json.DEFAULT;
try (var resp = client.get("/api/items").run();
InputStream in = resp.body().asStream();
- RecordReader r = m.fromArrayRecords(in)) {
+ RecordReader r = m.readArrayRecords(in)) {
while (r.canRead()) {
Item item = r.read(Item.class);
@@ -102,32 +102,35 @@ try (var resp = client.get("/api/items").run();
---
-## `fromTokens()` — fine-grained structural cursor
+## `readTokens()` — fine-grained structural cursor
For FULL-tier formats (JSON family, CBOR, MsgPack) you can open a
<a href="/site/apidocs/org/apache/juneau/marshall/stream/TokenReader.html"
target="_blank">TokenReader</a>
-and navigate the document structure token-by-token:
+and navigate the document structure token-by-token via a StAX-like
`next()`/`getCurrentToken()`
+cursor:
```java
Marshaller m = Json.DEFAULT;
try (var resp = client.get("/api/data").run();
InputStream in = resp.body().asStream();
- TokenReader r = m.fromTokens(in)) {
-
- r.startObject();
- while (r.hasMoreKeys()) {
- String key = r.key();
- String value = r.stringValue();
- System.out.println(key + " = " + value);
+ TokenReader r = m.readTokens(in)) {
+
+ while (r.next() != TokenType.END_OF_STREAM) {
+ switch (r.getCurrentToken()) {
+ case FIELD_NAME -> System.out.print(r.getFieldName() + " = ");
+ case VALUE_STRING -> System.out.println(r.getString());
+ case VALUE_NUMBER -> System.out.println(r.getNumber());
+ case VALUE_BOOLEAN -> System.out.println(r.getBool());
+ default -> { /* structural tokens: START_OBJECT, END_OBJECT,
START_ARRAY, END_ARRAY, VALUE_NULL */ }
+ }
}
- r.endObject();
}
```
> Only formats that implement
> <a href="/site/apidocs/org/apache/juneau/marshall/stream/TokenReadable.html"
> target="_blank">TokenReadable</a>
-> support `fromTokens()`. Calling it on a PARTIAL-tier format (e.g.
`Yaml.DEFAULT`) throws
+> support `readTokens()`. Calling it on a PARTIAL-tier format (e.g.
`Yaml.DEFAULT`) throws
> `ClassCastException` at runtime.
---
@@ -141,7 +144,7 @@ try (var resp = client.get("/api/events")
.header("Accept", "application/jsonl")
.run();
InputStream in = resp.body().asStream();
- RecordReader r = Jsonl.DEFAULT.fromRecords(in)) {
+ RecordReader r = Jsonl.DEFAULT.readRecords(in)) {
// Enhanced-for loop via Iterable adapter
for (Event e : (Iterable<Event>) () -> r.iterator(Event.class)) {
@@ -154,7 +157,7 @@ try (var resp = client.get("/api/events")
.header("Accept", "application/jsonl")
.run();
InputStream in = resp.body().asStream();
- RecordReader r = Jsonl.DEFAULT.fromRecords(in)) {
+ RecordReader r = Jsonl.DEFAULT.readRecords(in)) {
long count = r.stream(Event.class).filter(Event::isActive).count();
}
@@ -164,7 +167,7 @@ try (var resp = client.get("/api/events")
## Checking cursor quality
-`RecordReader.isRecordStreaming()` returns `true` when the cursor is a true
O(1) streaming
+`RecordReader.isStreaming()` returns `true` when the cursor is a true O(1)
streaming
cursor (constant memory per record) and `false` when it is backed by a
buffered adapter.
Check it before processing very large responses if memory is a concern:
@@ -175,9 +178,9 @@ try (var resp = client.get("/api/data")
.header("Accept", "application/yaml")
.run();
InputStream in = resp.body().asStream();
- RecordReader r = m.fromRecords(in)) {
+ RecordReader r = m.readRecords(in)) {
- boolean isStreaming = r.isRecordStreaming(); // false for YAML
+ boolean isStreaming = r.isStreaming(); // false for YAML
// ...
}
```
diff --git a/pages/topics/15.05.VarResolver.md
b/pages/topics/15.05.VarResolver.md
index 1d58562a10..f43ca3dfd9 100644
--- a/pages/topics/15.05.VarResolver.md
+++ b/pages/topics/15.05.VarResolver.md
@@ -30,10 +30,12 @@ in juneau-microservice-jetty. By default, support for the
following variables ar
- `$A{key[,default]}` - <a
href="/site/apidocs/org/apache/juneau/commons/svl/vars/ArgsVar.html"
target="_blank">ArgsVar</a>
- `$C{key[,default]}` - <a
href="/site/apidocs/org/apache/juneau/config/vars/ConfigVar.html"
target="_blank">ConfigVar</a>
- `$MF{key[,default]}` - <a
href="/site/apidocs/org/apache/juneau/commons/svl/vars/ManifestFileVar.html"
target="_blank">ManifestFileVar</a>
-- `$IF{arg,then[,else]}` - <a
href="/site/apidocs/org/apache/juneau/commons/svl/vars/IfVar.html"
target="_blank">IfVar</a>
-- `$SW{arg,pattern1:then1[,pattern2:then2...]}` - <a
href="/site/apidocs/org/apache/juneau/commons/svl/vars/SwitchVar.html"
target="_blank">SwitchVar</a>
-- `$CO{arg[,arg2...]}` - <a
href="/site/apidocs/org/apache/juneau/commons/svl/vars/CoalesceVar.html"
target="_blank">CoalesceVar</a>
-- `$PM{arg,pattern}` - <a
href="/site/apidocs/org/apache/juneau/commons/svl/vars/PatternMatchVar.html"
target="_blank">PatternMatchVar</a>
-- `$UC{arg}` - <a
href="/site/apidocs/org/apache/juneau/commons/svl/vars/UpperCaseVar.html"
target="_blank">UpperCaseVar</a>
-- `$LC{arg}` - <a
href="/site/apidocs/org/apache/juneau/commons/svl/vars/LowerCaseVar.html"
target="_blank">LowerCaseVar</a>
-- `$NE{arg}` - <a
href="/site/apidocs/org/apache/juneau/commons/svl/vars/NotEmptyVar.html"
target="_blank">NotEmptyVar</a>
\ No newline at end of file
+
+:::note
+Juneau 10.0.0 removed the 11 transformation/conditional `$Var{...}`-style
variables that previously
+lived here (`IfVar`, `SwitchVar`, `CoalesceVar`, `PatternMatchVar`,
`PatternReplaceVar`,
+`PatternExtractVar`, `UpperCaseVar`, `LowerCaseVar`, `NotEmptyVar`, `LenVar`,
`SubstringVar`) and
+replaced them with the `#{name(args...)}` function-call syntax — see
+[`#{...}` Function-call
Syntax](/docs/topics/SimpleVariableLanguage#-function-call-syntax) for the
+current equivalents (e.g. `#{if(...)}`, `#{switch(...)}`, `#{upper(...)}`,
`#{lower(...)}`).
+:::
\ No newline at end of file
diff --git a/pages/topics/18.02.ScClientOverview.md
b/pages/topics/18.02.ScClientOverview.md
index f8470fef1d..3c36a06213 100644
--- a/pages/topics/18.02.ScClientOverview.md
+++ b/pages/topics/18.02.ScClientOverview.md
@@ -42,16 +42,18 @@ Until the client library is implemented, you can consume
`juneau-sc-server` with
import org.apache.juneau.config.*;
import org.apache.juneau.config.store.*;
import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.marshall.json.*;
import org.apache.juneau.rest.client.*;
public class ScClientExample {
public static void main(String[] args) throws Exception {
- // Build a Juneau REST client targeting the sc-server
- RestClient client = RestClient.create()
+ // Build a Juneau REST client targeting the sc-server (NG client —
explicit JSON serializer/parser)
+ RestClient client = RestClient.builder()
.rootUrl("http://config-host:10000")
- .json()
+ .serializer(JsonSerializer.DEFAULT)
+ .parser(JsonParser.DEFAULT)
.build();
// Fetch config for project "my-service" on branch "main"
@@ -60,7 +62,7 @@ public class ScClientExample {
JsonMap response = client
.get("/configs/my-service/main")
.run()
- .getContent()
+ .body()
.as(JsonMap.class);
String appCfgText = response.get("APPLICATION",
JsonMap.class).getString("value");
diff --git a/pages/topics/20.JuneauExamples.md
b/pages/topics/20.JuneauExamples.md
index f67f9c8268..372f5e8630 100644
--- a/pages/topics/20.JuneauExamples.md
+++ b/pages/topics/20.JuneauExamples.md
@@ -11,23 +11,28 @@ The
[juneau-examples-core](https://github.com/apache/juneau/tree/master/juneau-e
```
juneau-examples/ (aggregator, packaging=pom)
-└── juneau-examples-core/ Standalone core-marshalling examples
(OSGi bundle)
- └── src/main/java/org/apache/juneau/examples/
- ├── core/pojo/ Shared sample beans (Pojo,
PojoComplex)
- ├── core/json/ JSON serialize/parse
- ├── core/xml/ XML serialize/parse
- ├── core/html/ HTML serialize/parse
- ├── core/uon/ UON serialize/parse
- ├── core/oapi/ OpenAPI HTTP-part serialization
- ├── core/svl/ Simple Variable Language (SVL)
- ├── core/config/store/ Custom ConfigStore (SqlStore
skeleton)
- ├── bean/ DTO builders (HTML5, ATOM, Swagger)
- ├── bean/atom/ ATOM feed marshalling (XML/JSON/HTML)
- ├── serializer/ Custom stream serializer
(ImageSerializer)
- └── parser/ Custom stream parser (ImageParser)
+├── juneau-examples-core/ Standalone core-marshalling examples
(OSGi bundle)
+│ └── src/main/java/org/apache/juneau/examples/
+│ ├── core/pojo/ Shared sample beans (Pojo,
PojoComplex)
+│ ├── core/json/ JSON serialize/parse
+│ ├── core/xml/ XML serialize/parse
+│ ├── core/html/ HTML serialize/parse
+│ ├── core/uon/ UON serialize/parse
+│ ├── core/oapi/ OpenAPI HTTP-part serialization
+│ ├── core/svl/ Simple Variable Language (SVL)
+│ ├── core/config/store/ Custom ConfigStore (SqlStore
skeleton)
+│ ├── bean/ DTO builders (HTML5, ATOM, Swagger)
+│ ├── bean/atom/ ATOM feed marshalling (XML/JSON/HTML)
+│ ├── serializer/ Custom stream serializer
(ImageSerializer)
+│ └── parser/ Custom stream parser (ImageParser)
+└── juneau-examples-mcp/ Model Context Protocol server/client
examples
+ └── src/main/java/org/apache/juneau/examples/mcp/
+ ├── (root)
ExampleServer/ExampleMcpServer/ExampleClient, NoteStore
+ ├── secured/ OAuth-secured MCP server/client +
OfflineAuthorizationServer
+ └── spring/ Spring Boot MCP server integration
```
-The module builds as an OSGi bundle and depends only on library Juneau modules
— there is no runtime server or web container involved.
+The `juneau-examples-core` module builds as an OSGi bundle and depends only on
library Juneau modules — there is no runtime server or web container involved.
`juneau-examples-mcp` is a separate, `Rest`-server-backed module; see [Juneau
MCP Recipes](/docs/topics/JuneauMcpRecipes#full-example-juneau-examples-mcp)
for its walkthrough.
## Getting started
@@ -113,6 +118,7 @@ Because the constructor is marked `@BeanCtor` with
`@Name`-annotated parameters,
<node-0><java-doc><a href="/docs/topics/JuneauBeanAtom"
target="_blank">juneau-bean-atom</a></java-doc> - The ATOM bean model used by
the ATOM examples</node-0>
<node-0><java-doc><a href="/docs/topics/JuneauConfig"
target="_blank">juneau-config</a></java-doc> - The configuration API behind
<code>SqlStore</code></node-0>
<node-0><java-doc><a href="/docs/topics/JuneauPetstore"
target="_blank">juneau-petstore</a></java-doc> - A full REST showcase
application building on these core APIs</node-0>
+<node-0><java-doc><a
href="/docs/topics/JuneauMcpRecipes#full-example-juneau-examples-mcp"
target="_blank">juneau-examples-mcp</a></java-doc> - MCP server/client examples
(including OAuth-secured and Spring Boot variants)</node-0>
<node-0><java-doc><a href="/docs/topics/JuneauEcosystemOverview"
target="_blank">Juneau Ecosystem Overview</a></java-doc> - How the modules fit
together</node-0>
</tree>
diff --git a/pages/topics/23.02.JuneauShadedRestClient.md
b/pages/topics/23.02.JuneauShadedRestClient.md
index fe3519e411..34400db8c3 100644
--- a/pages/topics/23.02.JuneauShadedRestClient.md
+++ b/pages/topics/23.02.JuneauShadedRestClient.md
@@ -40,21 +40,28 @@ Use `juneau-shaded-rest-client` when you need:
- **Jakarta XML Bind API 3.0+** - For XML serialization
- **Apache Jena** - For RDF support
+:::note
+The code samples below target the canonical next-generation (NG)
`org.apache.juneau.rest.client.*` client bundled in this shaded artifact — the
headline module per [What's Included](#whats-included) above. The NG client is
still Beta (see the `RestClient` javadoc) and has a deliberately smaller
surface than the classic client: no `.json()`/`.basicAuth()`/`.oauth2Bearer()`
shortcuts, no `RetryInterceptor`. If you need those, use the bundled
`juneau-rest-client-classic` (`org.apache.juneau [...]
+:::
+
## Example Usage
### Basic REST Call
```java
+import org.apache.juneau.marshall.json.*;
import org.apache.juneau.rest.client.*;
public class RestClientExample {
- public static void main(String[] args) {
- try (RestClient client = RestClient.create().build()) {
+ public static void main(String[] args) throws Exception {
+ try (RestClient client = RestClient.builder()
+ .parser(JsonParser.DEFAULT)
+ .build()) {
// GET request
Person person = client.get("http://api.example.com/person/1")
.run()
- .getContent().as(Person.class);
-
+ .body().as(Person.class);
+
System.out.println(person.getName());
}
}
@@ -64,15 +71,18 @@ public class RestClientExample {
### POST with JSON
```java
-try (RestClient client = RestClient.create().json().build()) {
+try (RestClient client = RestClient.builder()
+ .serializer(JsonSerializer.DEFAULT)
+ .parser(JsonParser.DEFAULT)
+ .build()) {
Person newPerson = new Person("John Smith", 42);
-
+
// POST JSON
Person created = client.post("http://api.example.com/person")
- .content(newPerson)
+ .body(newPerson)
.run()
- .getContent().as(Person.class);
-
+ .body().as(Person.class);
+
System.out.println("Created: " + created.getId());
}
```
@@ -85,15 +95,18 @@ try (RestClient client =
RestClient.create().json().build()) {
public interface PetStore {
@RemoteGet("/pet/{petId}")
Pet getPet(@Path("petId") long id);
-
+
@RemotePost("/pet")
Pet createPet(@Content Pet pet);
}
// Use it
-try (RestClient client = RestClient.create().build()) {
- PetStore store = client.getRemote(PetStore.class);
-
+try (RestClient client = RestClient.builder()
+ .serializer(JsonSerializer.DEFAULT)
+ .parser(JsonParser.DEFAULT)
+ .build()) {
+ PetStore store = client.remote(PetStore.class);
+
Pet pet = store.getPet(123);
System.out.println(pet.getName());
}
@@ -102,47 +115,45 @@ try (RestClient client = RestClient.create().build()) {
### Try-With-Resources Pattern
```java
-RestClient client = RestClient.create().build();
-
-try (RestRequest req = client.get("/api/data")) {
- String response = req.run().getContent().asString();
+try (RestClient client =
RestClient.builder().parser(JsonParser.DEFAULT).build()) {
+ String response = client.get("/api/data").run().body().asString();
System.out.println(response);
-} // Request and response are automatically closed
+} // Client (and its underlying transport) is automatically closed
```
### Authentication
-```java
-// Basic authentication
-RestClient client = RestClient.create()
- .basicAuth("user", "password")
- .build();
+The NG client has no built-in auth shortcuts (classic-only, per the note
above); wire credentials in as plain headers:
-// Bearer token
-RestClient client = RestClient.create()
- .headerData("Authorization", "Bearer " + token)
+```java
+// Basic authentication (manual, since there's no NG .basicAuth() shortcut)
+String creds =
Base64.getEncoder().encodeToString("user:password".getBytes(StandardCharsets.UTF_8));
+RestClient client = RestClient.builder()
+ .header("Authorization", "Basic " + creds)
.build();
-// OAuth2
-RestClient client = RestClient.create()
- .oauth2Bearer(tokenSupplier)
+// Bearer token — a Supplier re-evaluates the header on every request (e.g.
for token refresh)
+RestClient client2 = RestClient.builder()
+ .header("Authorization", () -> "Bearer " + tokenSupplier.get())
.build();
```
### Interceptors
+`RestCallInterceptor` is a lifecycle callback interface
(`onInit`/`onConnect`/`onClose`), not a per-request lambda — and there is no
built-in `RetryInterceptor` on either client stack:
+
```java
-RestClient client = RestClient.create()
- .interceptors(
- // Log requests
- (req) -> System.out.println("Calling: " + req.getUri()),
-
- // Add custom headers
- (req) -> req.header("X-Custom", "value"),
-
- // Retry logic
- RetryInterceptor.create().maxAttempts(3).build()
- )
+RestClient client = RestClient.builder()
+ .interceptors(new RestCallInterceptor() {
+ @Override
+ public void onInit(RestRequest req) {
+ System.out.println("Calling: " + req.getUri());
+ }
+ @Override
+ public void onConnect(RestRequest req, RestResponse res) {
+ System.out.println("Status: " + res.getStatusCode());
+ }
+ })
.build();
```
@@ -155,13 +166,13 @@ import org.apache.juneau.rest.mock.*;
@Test
public void testRestClient() throws Exception {
- MockRestClient client = MockRestClient.create(MyRestApi.class).build();
-
+ MockRestClient client = MockRestClient.create(MyRestApi.class);
+
// Call the mocked API
String response = client.get("/test")
.run()
- .getContent().asString();
-
+ .body().asString();
+
assertEquals("expected", response);
}
```
@@ -169,38 +180,38 @@ public void testRestClient() throws Exception {
## Features
### Content Negotiation
-Automatic serialization/deserialization based on `Accept` and `Content-Type`
headers:
+Automatic serialization/deserialization based on `Accept` and `Content-Type`
headers (there is no dedicated `.accept(String)` shortcut on the NG client —
set the header directly):
```java
// Request JSON, get POJO
Person person = client.get("/person/1")
- .accept("application/json")
+ .header("Accept", "application/json")
.run()
- .getContent().as(Person.class);
+ .body().as(Person.class);
```
### Error Handling
```java
-try (RestClient client = RestClient.create().build()) {
+try (RestClient client = RestClient.builder().build()) {
String response = client.get("/api/data")
.run()
- .assertStatus().is(200) // Assert successful
- .getContent().asString();
+ .assertOk() // Throws RestCallException if status is not 2xx
+ .body().asString();
} catch (RestCallException e) {
- System.err.println("HTTP " + e.getResponseCode());
- System.err.println(e.getResponseMessage());
+ System.err.println("HTTP " + e.getStatusCode());
}
```
### Request/Response Assertions
```java
-client.get("/api/data")
- .run()
- .assertStatus().is(200)
- .assertHeader("Content-Type").is("application/json")
- .assertContent().contains("expected");
+try (RestResponse resp = client.get("/api/data").run()) {
+ resp.assertThat()
+ .statusCode(200)
+ .header("Content-Type").contains("application/json")
+ .body().contains("expected");
+}
```
## Performance Characteristics
diff --git a/pages/topics/23.03.JuneauShadedRestServer.md
b/pages/topics/23.03.JuneauShadedRestServer.md
index 800da2f083..cbccac2366 100644
--- a/pages/topics/23.03.JuneauShadedRestServer.md
+++ b/pages/topics/23.03.JuneauShadedRestServer.md
@@ -281,26 +281,26 @@ public class MyApi extends BasicRestServlet {
The mock framework lives in `juneau-rest-mock`, which is **not** bundled in
this shaded artifact. Add it as a separate test-scoped dependency to use
`MockRestClient`:
```java
+import org.apache.juneau.rest.mock.*;
+
@Test
public void testRestEndpoint() throws Exception {
- MockRestClient client = MockRestClient.create(MyRestApi.class)
- .json()
- .build();
-
+ MockRestClient client = MockRestClient.create(MyRestApi.class);
+
// Test GET
Person person = client.get("/person/1")
.run()
- .assertStatus().is(200)
- .getContent().as(Person.class);
-
+ .assertOk()
+ .body().as(Person.class);
+
assertEquals("John", person.getName());
-
+
// Test POST
Person newPerson = new Person("Jane", 30);
client.post("/person")
- .content(newPerson)
+ .body(newPerson)
.run()
- .assertStatus().is(201);
+ .assertStatus(201);
}
```
diff --git a/pages/topics/23.05.JuneauShadedAll.md
b/pages/topics/23.05.JuneauShadedAll.md
index 88abbfd091..b01c2cb513 100644
--- a/pages/topics/23.05.JuneauShadedAll.md
+++ b/pages/topics/23.05.JuneauShadedAll.md
@@ -132,26 +132,30 @@ public class MyApi extends BasicRestServlet {
}
}
-// Client-side consumption
+// Client-side consumption (NG client — no .json() shortcut, so wire the JSON
serializer/parser explicitly)
+import org.apache.juneau.marshall.json.*;
+import org.apache.juneau.rest.client.*;
+
public class MyClient {
- public static void main(String[] args) {
- try (RestClient client = RestClient.create()
- .json()
+ public static void main(String[] args) throws Exception {
+ try (RestClient client = RestClient.builder()
+ .serializer(JsonSerializer.DEFAULT)
+ .parser(JsonParser.DEFAULT)
.rootUrl("http://localhost:8080")
.build()) {
-
+
// GET users
List<User> users = client.get("/api/users")
.run()
- .getContent().as(User[].class);
-
+ .body().as(User[].class);
+
// POST new user
User newUser = new User("John", "[email protected]");
User created = client.post("/api/users")
- .content(newUser)
+ .body(newUser)
.run()
- .getContent().as(User.class);
-
+ .body().as(User.class);
+
System.out.println("Created: " + created.getName());
}
}
@@ -210,15 +214,13 @@ public void testCompleteWorkflow() throws Exception {
User user = new User("John", "[email protected]");
// REST API testing
- MockRestClient client = MockRestClient.create(MyApi.class)
- .json()
- .build();
+ MockRestClient client = MockRestClient.create(MyApi.class);
User created = client.post("/users")
- .content(user)
+ .body(user)
.run()
- .assertStatus().is(201)
- .getContent().as(User.class);
+ .assertStatus(201)
+ .body().as(User.class);
assertEquals("John", created.getName());
}
@@ -235,9 +237,9 @@ public void testCompleteWorkflow() throws Exception {
### REST Client
- Fluent API
- Remote proxies
-- Authentication (Basic, Bearer, OAuth2, Custom)
-- Interceptors and retries
-- Connection pooling
+- Pluggable HTTP transports (JDK `HttpClient`, Apache HttpClient, OkHttp,
Jetty client)
+- Lifecycle interceptors (`RestCallInterceptor`)
+- Connection pooling (via the underlying transport)
### REST Server
- Annotation-based routing
diff --git a/src/pages/downloads.md b/src/pages/downloads.md
index ab670d24df..936fcdf5c8 100644
--- a/src/pages/downloads.md
+++ b/src/pages/downloads.md
@@ -18,34 +18,35 @@
- `juneau-shaded-rest-server-springboot.jar` - REST server + Spring Boot
integration (3.8 MB)
- `juneau-shaded-all.jar` - Complete framework in one JAR (4.0 MB)
- **`/lib`** - Individual Jars
+ - `juneau-commons.jar` - Bean metadata framework, SVL string-variable
resolution, and shared utilities (SVL now lives here — there is no separate
`juneau-svl.jar`)
- `juneau-marshall.jar` - Marshalling
- - `juneau-marshall-rdf.jar` - Marshalling RDF extension (requires Apache
Jena 2.7.1+)
+ - `juneau-marshall-rdf.jar` - Marshalling RDF extension (requires Apache
Jena)
+ - `juneau-config.jar` - Config File
+ - `juneau-test.jar` - Fluent assertions, Bean-Centric Testing, and JUnit 5
extensions
- `juneau-bean-atom.jar` - ATOM Data Transfer Object Beans
- `juneau-bean-common.jar` - Common Data Transfer Object Beans
- `juneau-bean-html5.jar` - HTML5 Data Transfer Object Beans
- `juneau-bean-jsonschema.jar` - JSON Schema Data Transfer Object Beans
- `juneau-bean-openapi-v3.jar` - OpenAPI 3 Data Transfer Object Beans
- `juneau-bean-swagger-v2.jar` - Swagger 2 Data Transfer Object Beans
- - `juneau-svl.jar` - Simple Variable Language
- - `juneau-config.jar` - Config File
- - `juneau-rest-server.jar` - REST Servlet
- - `juneau-rest-server-jaxrs.jar` - REST Servlet JAX/RS extension
- - `juneau-rest-client.jar` - REST Client (requires Apache HttpClient 4.5+)
+ - `juneau-rest-server.jar` - REST Servlet (there is no
`juneau-rest-server-jaxrs` module)
+ - `juneau-rest-server-springboot.jar` - REST Spring Boot integration
+ - `juneau-rest-client.jar` - Canonical, transport-agnostic REST client
- `juneau-rest-mock.jar` - REST mock testing API
- **`/osgi`** - OSGi Libraries
+ - `org.apache.juneau.commons.jar` - Bean metadata framework, SVL, and shared
utilities
- `org.apache.juneau.marshall.jar` - Marshalling
- `org.apache.juneau.marshall.rdf.jar` - Marshalling RDF extension
+ - `org.apache.juneau.config.jar` - Config File
+ - `org.apache.juneau.test.jar` - Fluent assertions and Bean-Centric Testing
- `org.apache.juneau.bean.atom.jar` - ATOM Data Transfer Object Beans
- `org.apache.juneau.bean.common.jar` - Common Data Transfer Object Beans
- `org.apache.juneau.bean.html5.jar` - HTML5 Data Transfer Object Beans
- `org.apache.juneau.bean.jsonschema.jar` - JSON Schema Data Transfer Object
Beans
- `org.apache.juneau.bean.openapi3.jar` - OpenAPI 3 Data Transfer Object
Beans
- - `org.apache.juneau.svl.jar` - Simple Variable Language
- - `org.apache.juneau.config.jar` - Config File
- `org.apache.juneau.rest.server.jar` - REST Servlet
- - `org.apache.juneau.rest.server.jaxrs.jar` - REST Servlet JAX/RS extension
- - `org.apache.juneau.rest.server.springboot` - REST Spring Boot integration
+ - `org.apache.juneau.rest.server.springboot.jar` - REST Spring Boot
integration
- `org.apache.juneau.rest.client.jar` - REST Client
- `org.apache.juneau.rest.mock.jar` - REST mock testing API
@@ -66,14 +67,14 @@
<!-- ...where the artifactId is typically... -->
<artifactId>juneau-shaded-all</artifactId>
-<!-- ...but can also be any of the following... -->
+<!-- ...but can also be any of the following (not exhaustive — see the
+ Ecosystem table on the "About" page for the complete, current module
list)... -->
<!-- Core modules -->
-<artifactId>juneau-test</artifactId>
<artifactId>juneau-commons</artifactId>
<artifactId>juneau-config</artifactId>
<artifactId>juneau-marshall</artifactId>
<artifactId>juneau-marshall-rdf</artifactId>
-<artifactId>juneau-svl</artifactId>
+<artifactId>juneau-test</artifactId>
<!-- Bean DTOs -->
<artifactId>juneau-bean-atom</artifactId>
<artifactId>juneau-bean-common</artifactId>
@@ -97,6 +98,8 @@
<artifactId>juneau-shaded-rest-server-springboot</artifactId>
```
+See the [Ecosystem table](/about#ecosystem) on the About page for the
complete, current list of publishable modules — this page only calls out the
most commonly-used ones.
+
## Verifying File Integrity
How to [verify downloaded
files](https://www.apache.org/info/verification.html).