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 8a48a92e10 docs: add 9.5.0 release notes + topic pages for Problem 
Details, SSE, and OpenAPI 3.1
8a48a92e10 is described below

commit 8a48a92e106c08af61f28f5efd6264cc067930b8
Author: James Bognar <[email protected]>
AuthorDate: Fri May 22 20:45:09 2026 -0400

    docs: add 9.5.0 release notes + topic pages for Problem Details, SSE, and 
OpenAPI 3.1
    
    Covers:
    - TODO-61: RFC 7807 / 9457 Problem-Details server-side wiring
      * New topic page pages/topics/10.20a.RestServerProblemDetails.md
      * Sidebar entry
      * Release notes section
    - TODO-62: Server-side SSE helpers (SseResponseSupport, SseBroadcaster, 
SseHeartbeat, arg injection)
      * New topic page pages/topics/10.08.RestServerSse.md
      * Sidebar entry
      * Release notes section
    - TODO-63: OpenAPI 3.1 emission + @Rest(apiFormat=...) knob
      * Rewrote pages/topics/10.16.02.BasicRestServletSwagger.md to cover both 
/api and /openapi
        endpoints, the three apiFormat modes (swagger/openapi/both), 
components.schemas reuse,
        and the ?Swagger / ?OpenApi query mirrors on group resources
      * Migration guide rows added in pages/topics/23.01.V9.5-migration-guide.md
      * Release notes block describing the spec, resolution precedence, and 
follow-up landings
    
    Co-authored-by: Cursor <[email protected]>
---
 pages/release-notes/9.5.0.md                     | 143 +++++++++-
 pages/topics/10.08.RestServerSse.md              |  48 ++++
 pages/topics/10.16.02.BasicRestServletSwagger.md |  93 ++++++-
 pages/topics/10.20a.RestServerProblemDetails.md  | 328 +++++++++++++++++++++++
 pages/topics/23.01.V9.5-migration-guide.md       | 233 +++++++++++++++-
 sidebars.ts                                      |  10 +
 6 files changed, 842 insertions(+), 13 deletions(-)

diff --git a/pages/release-notes/9.5.0.md b/pages/release-notes/9.5.0.md
index 1dca23691a..bf119275b1 100644
--- a/pages/release-notes/9.5.0.md
+++ b/pages/release-notes/9.5.0.md
@@ -6,7 +6,7 @@ title: "Release 9.5.0"
 
 **Date:** TBD
 
-Juneau 9.5.0 is a minor release with native TOML and YAML support, BSON 
(Binary JSON) support for MongoDB-interoperable binary serialization, CBOR 
(Concise Binary Object Representation) per RFC 8949 for IoT and constrained 
environments, full CSV serializer/parser support, JCS (JSON Canonicalization 
Scheme) per RFC 8785 for deterministic hashing and signing, RDF/THRIFT and 
RDF/PROTO binary format support, native serialization support for 
lazy-evaluated sequence types, large-dataset stream [...]
+Juneau 9.5.0 is a minor release with native OpenAPI 3.1 emission (alongside 
Swagger v2, with a per-resource `@Rest(apiFormat=…)` knob), native TOML and 
YAML support, BSON (Binary JSON) support for MongoDB-interoperable binary 
serialization, CBOR (Concise Binary Object Representation) per RFC 8949 for IoT 
and constrained environments, full CSV serializer/parser support, JCS (JSON 
Canonicalization Scheme) per RFC 8785 for deterministic hashing and signing, 
RDF/THRIFT and RDF/PROTO binary f [...]
 
 ### juneau-marshall
 
@@ -1986,6 +1986,41 @@ String name
 
 ### juneau-rest-server
 
+#### OpenAPI 3.1 Emission + `apiFormat` Knob (TODO-63)
+
+`juneau-rest-server` now ships first-class OpenAPI 3.1 document generation 
alongside the existing Swagger v2 path:
+
+- New `org.apache.juneau.rest.openapi` package with `OpenApiProvider` SPI, 
`BasicOpenApiProvider`, and `BasicOpenApiProviderSession`. The session 
generates OpenAPI 3.1 via a JSON-level transformation of the existing Swagger 
2.0 emission, so every existing Swagger-aware annotation surface (`@Schema`, 
`@Content`, `@StatusCode`, etc.) round-trips to the OpenAPI document with no 
source changes.
+- New `RestRequest.getOpenApi()` accessor and 
`RestContext.getOpenApiProvider()` / `RestContext.getOpenApi(Locale)` parallel 
the Swagger family.
+- New `RedocUI` swap (in `juneau-bean-openapi-v3`) renders OpenAPI 3.1 
documents as a two-column Redoc-style HTML view for `text/html` requests.
+- New SVL variable `$OS{path}` mirrors `$SS{path}` for OpenAPI document 
lookups.
+- New `@Rest(openApiProvider=X.class)` attribute lets resources select a 
custom provider.
+
+The new `@Rest(apiFormat="…")` attribute selects which spec format the 
canonical `/api/*` endpoint serves (and whether the new `/openapi/*` sibling is 
mounted):
+
+| Value | `/api/*` | `/openapi/*` |
+|-------|----------|--------------|
+| `"swagger"` (default) | Swagger v2 + Swagger UI for `text/html` | 404 |
+| `"openapi"` | 404 | OpenAPI 3.1 + Redoc for `text/html` |
+| `"both"` | Swagger v2 + Swagger UI | OpenAPI 3.1 + Redoc |
+
+Resolution precedence: `@Rest(apiFormat=…)` (most-derived non-empty value 
wins) → system property `juneau.rest.apiFormat` → default `"swagger"`. The 
default is `"swagger"` so existing 9.4.x resources keep their pre-9.5.0 surface 
unchanged. Surface via `RestContext.getApiFormat()`. Constants live on 
`RestServerConstants` (`API_FORMAT_SWAGGER`, `API_FORMAT_OPENAPI`, 
`API_FORMAT_BOTH`, `SYSPROP_apiFormat`).
+
+Three additional pieces close out the TODO-63 scope:
+
+- **`components.schemas` reuse.** `BasicOpenApiProviderSession` now runs an 
explicit dedup pass after the Swagger-2.0-to-OpenAPI-3.1 transform: any inline 
schema that appears two or more times under operation parameter, request-body, 
or response-content slots is lifted into `components.schemas` and each 
occurrence is rewritten to a `{"$ref":"#/components/schemas/<name>"}` pointer. 
Names are derived from the schema's `title` (when present and unique) and fall 
back to synthesized `Schema<N [...]
+- **`?Swagger` / `?OpenApi` query mirrors on group resources.** 
`BasicGroupOperations` now defines two extra `GET /` overloads gated by 
`HasSwaggerQueryParam` / `HasOpenApiQueryParam` matchers, so 
`BasicRestServletGroup`, `BasicRestObjectGroup`, and 
`BasicSpringRestServletGroup` will serve the Swagger v2 or OpenAPI 3.1 document 
inline when a `?Swagger=…` or `?OpenApi=…` query parameter is present. Without 
those parameters, `getChildren(RestRequest)` continues to return the standard 
`Chil [...]
+- **YAML round-trip coverage.** `OpenApiYamlRoundTrip_Test` exercises 
`YamlSerializer.DEFAULT_READABLE` → `YamlParser.DEFAULT` over (a) a hand-built 
`OpenApi` bean, (b) the live document produced by a `BasicRestServlet`-based 
resource with `apiFormat="openapi"`, and (c) the `/openapi/*` endpoint served 
with `Accept: application/yaml`. Each path asserts structural equality across 
`openapi`, `info`, `servers`, `paths`, and `components.schemas`.
+
+#### Server-side SSE Helpers (TODO-62)
+
+`juneau-rest-server` now includes an SSE helper layer for streaming endpoints:
+
+- `RestResponse.sse()` returns `SseResponseSupport` for fluent event/comment 
writes (`sendEvent(...)`, `comment(...)`, `flush()`, `sendFrom(...)`).
+- `SseBroadcaster` / `SseSubscription` provide in-memory fan-out for 
per-connection subscriptions with bounded subscriber queues.
+- `SseHeartbeat` provides optional scheduled `: ping` comments (when a 
`ScheduledExecutorService` bean is available).
+- New `RestOpArg` resolvers allow direct `SseBroadcaster` and 
`SseSubscription` method-parameter injection in `@RestOp` handlers.
+
 #### Dynamic Child Resources (TODO-33)
 
 Parent resources that extend `BasicRestServletGroup` or `BasicRestObjectGroup` 
can now mount and unmount children at runtime, in addition to declaring them 
statically via `@Rest(children=…)`.
@@ -2195,6 +2230,103 @@ If you previously relied on `@Bean` (inject) overriding 
a Spring `@Bean`, you ha
 - **`RestResponse.downloadAs(String filename)`** — Sets `Content-Disposition: 
attachment` with a quoted `filename` parameter so browsers typically download 
the response body (e.g. `res.downloadAs("example.pdf").setContent(bytes)`).
 - **`ContentDisposition.attachment(String)`** and 
**`HttpHeaders.contentDispositionAttachment(String)`** (juneau-rest-common) — 
Build a safe header value (escapes `\` and `"` in the filename; rejects 
null/blank names and CR/LF). RFC 5987 `filename*` is not set; use a custom 
header value if you need full Unicode filenames.
 
+#### RFC 7807 / 9457 Problem-Details server-side wiring
+
+Juneau REST resources can now emit `application/problem+json` (RFC 7807 / 
9457) responses end-to-end. The wiring complements the `juneau-bean-rfc7807` 
module shipped earlier in this release (the typed `Problem` bean) by adding a 
response processor, an opt-in annotation flag, a `ProblemException` throw seam, 
and a `ProblemMapper` SPI for custom exception-to-`Problem` translation.
+
+##### Opt-in flag — `@Rest(problemDetails)` / per-op fanout
+
+- **`@Rest(problemDetails)`** — `String` attribute, tri-state (`"true"` / 
`"false"` / `""` inherits). When `"true"`, uncaught `BasicHttpException`s on 
the resource are rendered as `application/problem+json` regardless of the 
client `Accept` header (RFC 7807 §3 — the spec encourages this on errors).
+- **`@RestGet(problemDetails)` / `@RestPost(problemDetails)` / 
`@RestPut(problemDetails)` / `@RestPatch(problemDetails)` / 
`@RestDelete(problemDetails)` / `@RestOptions(problemDetails)` / 
`@RestOp(problemDetails)`** — same tri-state attribute on every method-level 
annotation. Per-op `"true"` opts an op in on a non-opted-in resource; per-op 
`"false"` opts an op out of an opted-in resource; `""` inherits the 
resource-level value.
+
+```java
+@Rest(path="/orders", problemDetails="true")
+public class OrderResource {
+
+    @RestGet("/{id}")
+    public Order get(@Path long id) {
+        throw new NotFound("Order {0} not found", id);  // → 404 
application/problem+json
+    }
+
+    @RestPost
+    public Problem create(Order in) {
+        if (in.balance < in.amount)
+            return Problem.fromStatus(403, "Insufficient credit", "Balance " + 
in.balance + " < amount " + in.amount)
+                .setType(URI.create("https://example.com/probs/out-of-credit";))
+                .set("balance", in.balance);
+        return null;
+    }
+}
+```
+
+##### `ProblemDetailsProcessor` (response processor)
+
+- **`org.apache.juneau.rest.processor.ProblemDetailsProcessor`** — slotted 
into the default `responseProcessors` chain after `ThrowableProcessor`. Handles 
four content shapes:
+  - A returned `Problem` bean — serialized directly. Honors client `Accept` 
strictly.
+  - A returned or thrown `ProblemException` — unwrapped via 
`ProblemException.getProblem()`. Honors `Accept` on non-opted-in ops 
(back-compat); flips to error-path semantics (ignores `Accept`) on opted-in ops.
+  - A thrown `BasicHttpException` — gated by `isProblemDetails()` (per-op 
resolution); on error path ignores `Accept` and adapts via the registered 
`ProblemMapper` (if any matches) or `ProblemAdapters.fromException(...)` 
otherwise.
+  - Any other thrown `Throwable` — only when an explicit `ProblemMapper` 
matches its class hierarchy. Mappers that return `null` are skipped and the 
chain continues; if no mapper matches, the processor returns `NEXT`.
+- Status precedence — when `Problem.getStatus()` is non-`null`, the processor 
calls `RestResponse.setStatus(int)` with that value. Otherwise it leaves the 
existing status alone (`RestSession.run()` normalizes `0` → `200` so per-op 
default codes still flow through).
+- `type` field policy — serialized as-is. A `null` `type` is omitted from the 
JSON output rather than synthesized as `"type":"about:blank"` (preserves the 
absent-vs-explicit distinction).
+
+##### `ProblemException` throw seam
+
+- **`org.apache.juneau.bean.rfc7807.ProblemException`** — `RuntimeException` 
carrying a `Problem` payload. Lets handlers throw a custom problem without 
manually building a `BasicHttpException`:
+
+```java
+throw new ProblemException(
+    Problem.fromStatus(403, "Insufficient credit", "Balance 30 < cost 50")
+        .setType(URI.create("https://example.com/probs/out-of-credit";))
+        .set("balance", 30)
+        .set("accounts", List.of("/account/12345", "/account/67890")));
+```
+
+  Lives in `juneau-bean-rfc7807` (the bean module stays free of any 
`juneau-rest-server` dep). The processor unwraps it via `instanceof 
ProblemException`.
+
+##### `ProblemAdapters` static helper
+
+- 
**`org.apache.juneau.bean.rfc7807.adapter.ProblemAdapters#fromException(BasicHttpException)`**
 — lives in `juneau-rest-common`. Adapts a `BasicHttpException` (status code, 
reason phrase, `getMessage()`) into a `Problem` bean. Used by the processor's 
default error-path fallback when no `ProblemMapper` matches. Available for 
direct caller use as well.
+
+##### `ProblemMapper` SPI — declarative exception translation
+
+- **`org.apache.juneau.bean.rfc7807.ProblemMapper<T extends Throwable>`** — 
pluggable interface lets users map arbitrary throwables into custom `Problem` 
shapes without writing a processor. Two methods: `Class<T> getExceptionType()` 
(used for hierarchy-aware dispatch) and `Problem map(T exception)`. Returning 
`null` defers to the next-most-specific mapper, and ultimately to the built-in 
`ProblemAdapters.fromException(...)` fallback for `BasicHttpException` 
subclasses.
+- **`org.apache.juneau.bean.rfc7807.ProblemMapperList`** — aggregator bean for 
registering more than one mapper on the same resource. The Juneau `@Bean` walk 
pairs each `@Bean` factory with its declared return type, so multiple `@Bean 
public ProblemMapper foo()` methods on one class collapse onto the single 
`ProblemMapper.class` slot; wrap them in a `ProblemMapperList` to keep them all 
reachable. The single-mapper case (`@Bean public ProblemMapper<MyException> 
mapper()`) keeps working un [...]
+
+```java
+public class InsufficientCreditMapper implements 
ProblemMapper<InsufficientCreditException> {
+    public Class<InsufficientCreditException> getExceptionType() { return 
InsufficientCreditException.class; }
+    public Problem map(InsufficientCreditException e) {
+        return Problem.fromStatus(403, "Insufficient credit", e.getMessage())
+            .setType(URI.create("https://example.com/probs/out-of-credit";))
+            .set("balance", e.getBalance())
+            .set("cost", e.getCost());
+    }
+}
+
+@Rest(problemDetails="true")
+public class AccountResource {
+    // Single-mapper case.
+    @Bean public ProblemMapper<InsufficientCreditException> creditMapper() { 
return new InsufficientCreditMapper(); }
+
+    // Multi-mapper case.
+    @Bean public ProblemMapperList problemMappers() {
+        return ProblemMapperList.of(new InsufficientCreditMapper(), new 
OrderNotFoundMapper());
+    }
+}
+```
+
+##### `ProblemLocalizationStrategy` — future-work seam
+
+- **`org.apache.juneau.bean.rfc7807.ProblemLocalizationStrategy`** — 
functional-interface SPI seam for locale-aware translation of `Problem.title` / 
`Problem.detail`. The default in-tree implementation is 
`ProblemLocalizationStrategy.IDENTITY` (pass-through); the processor consults 
the bean store for a registered strategy on every emission and falls back to 
`IDENTITY` when none is present, so the seam is invisible at zero cost on the 
hot path until a deliberate strategy is contributed.
+- A reference `Messages`-driven (resource-bundle) implementation is 
intentionally **out of scope for this release**. The `IDENTITY` default keeps 
the call-site contract stable so a future implementation can be dropped in 
without changing the processor.
+
+##### `Accept` negotiation policy
+
+- **Error path** (thrown `BasicHttpException` / thrown `ProblemException` / 
mapped `Throwable` on opted-in ops): emits `application/problem+json` 
regardless of the client `Accept` — the spec encourages this on errors.
+- **Success path** (returned `Problem` / returned `ProblemException`): honors 
the client `Accept` strictly. The processor only emits 
`application/problem+json` if `Accept` matches it (or `*/*`); otherwise it 
passes through to the next processor in the chain unchanged.
+
+See [REST Server — RFC 7807 Problem 
Details](/docs/topics/RestServerProblemDetails) for the full topic, including 
worked examples for the resource-level opt-in, the per-op fanout, the 
`ProblemException` throw path, and `ProblemMapper` registration.
+
 ### juneau-rest-client
 
 #### REST session option wire helpers
@@ -2791,6 +2923,15 @@ Problem back = JsonParser.DEFAULT.parse(json, 
Problem.class);
 
 The matching `Content-Type` constants (`ContentType.APPLICATION_PROBLEM_JSON` 
and `ContentType.APPLICATION_PROBLEM_XML`) have shipped in `juneau-rest-common` 
since 9.2.x; this module closes the loop by adding the typed wire bean.
 
+#### Throw + map SPIs
+
+The module also ships the seams used by the new `juneau-rest-server` 
problem-details wiring (see [RFC 7807 / 9457 Problem-Details server-side 
wiring](#rfc-7807--9457-problem-details-server-side-wiring) above):
+
+- **`ProblemException`** — `RuntimeException` carrying a `Problem`. Lets 
handlers throw a custom problem without manually building a 
`BasicHttpException`. The bean module owns this type so the 
`juneau-rest-server` processor can unwrap it without a reverse dependency.
+- **`ProblemMapper<T extends Throwable>`** — pluggable interface for 
translating arbitrary throwables into `Problem` shapes; the server-side 
processor discovers it via the resource bean store and dispatches in 
most-specific-first order across the exception hierarchy.
+- **`ProblemMapperList`** — aggregator bean for registering more than one 
mapper on the same resource (works around the single-slot collapse of multiple 
`@Bean public ProblemMapper foo()` factories that share a return type).
+- **`ProblemLocalizationStrategy`** — functional-interface SPI seam for future 
locale-aware translation of `Problem.title` / `Problem.detail`. Defaults to 
`ProblemLocalizationStrategy.IDENTITY` (pass-through). A reference `Messages` 
(resource-bundle) implementation is intentionally deferred to a future release.
+
 See [juneau-bean-rfc7807](/docs/topics/JuneauBeanRfc7807) for the full topic.
 
 ### juneau-bean-hal (new module)
diff --git a/pages/topics/10.08.RestServerSse.md 
b/pages/topics/10.08.RestServerSse.md
new file mode 100644
index 0000000000..7837867e57
--- /dev/null
+++ b/pages/topics/10.08.RestServerSse.md
@@ -0,0 +1,48 @@
+---
+title: "Server-Sent Events"
+slug: RestServerSse
+---
+
+Juneau REST Server now includes SSE response helpers for event-at-a-time 
streaming endpoints.
+
+### `RestResponse.sse()` helper
+
+Use `RestResponse.sse()` to configure an SSE response and emit events without 
manually juggling writer state:
+
+```java
+@RestGet(path="/stream", serializers=SseSerializer.class)
+public void stream(RestResponse res) throws Exception {
+    try (var sse = res.sse().heartbeat(Duration.ofSeconds(15))) {
+        sse.sendEvent("tick", "one");
+        sse.comment("ping");
+        sse.sendEvent(new SseEvent("tick", "two"));
+        sse.flush();
+    }
+}
+```
+
+The helper sets:
+
+- `Content-Type: text/event-stream`
+- `Cache-Control: no-cache`
+- `X-Content-Type-Options: nosniff`
+- `Content-Encoding: identity`
+
+### Broadcaster pattern
+
+Use `SseBroadcaster` and `SseSubscription` for fan-out from one producer to 
many subscribers.
+
+```java
+@RestGet(path="/broadcast", serializers=SseSerializer.class)
+public void broadcast(RestRequest req, RestResponse res, SseBroadcaster 
broadcaster) throws Exception {
+    var id = req.getHttpServletRequest().getRequestId();
+    var subscription = broadcaster.subscribe(id);
+    try (var sse = res.sse().heartbeat(Duration.ofSeconds(15))) {
+        sse.sendFrom(subscription);
+    } finally {
+        subscription.close();
+    }
+}
+```
+
+When a subscriber queue is full, the in-memory broadcaster drops the oldest 
event so recent events continue to flow.
diff --git a/pages/topics/10.16.02.BasicRestServletSwagger.md 
b/pages/topics/10.16.02.BasicRestServletSwagger.md
index f53933440d..5099fe1afb 100644
--- a/pages/topics/10.16.02.BasicRestServletSwagger.md
+++ b/pages/topics/10.16.02.BasicRestServletSwagger.md
@@ -1,9 +1,11 @@
 ---
-title: "BasicRestServlet/BasicRestObject Swagger"
+title: "BasicRestServlet/BasicRestObject Swagger and OpenAPI 3.1"
 slug: BasicRestServletSwagger
 ---
 
-Any subclass of <a 
href="/site/apidocs/org/apache/juneau/rest/servlet/BasicRestServlet.html" 
target="_blank">BasicRestServlet</a> and <a 
href="/site/apidocs/org/apache/juneau/rest/servlet/BasicRestObject.html" 
target="_blank">BasicRestObject</a> gets an auto-generated Swagger UI when 
performing an `OPTIONS` request with `Accept:text/html` due to the following 
method:
+Any subclass of <a 
href="/site/apidocs/org/apache/juneau/rest/servlet/BasicRestServlet.html" 
target="_blank">BasicRestServlet</a> and <a 
href="/site/apidocs/org/apache/juneau/rest/servlet/BasicRestObject.html" 
target="_blank">BasicRestObject</a> gets an auto-generated API documentation 
page on `GET /api/*` (Swagger v2 by default) and on `GET /openapi/*` (OpenAPI 
3.1 when enabled). For an `Accept: text/html` request the document is rendered 
through Swagger UI or Redoc respectively; other  [...]
+
+The default endpoint is wired by the following two `default` methods on 
`BasicRestOperations`:
 
 ```java
 @RestGet(
@@ -30,16 +32,87 @@ Any subclass of <a 
href="/site/apidocs/org/apache/juneau/rest/servlet/BasicRestS
         SwaggerUI.class
     }
 )
-@Override /* BasicRestOperations */
-public Swagger getSwagger(RestRequest req) {
+default Swagger getSwagger(RestRequest req) {
+    if (API_FORMAT_OPENAPI.equals(req.getContext().getApiFormat()))
+        throw new NotFound();
     return req.getSwagger().orElseThrow(NotFound::new);
 }
+
+@RestGet(
+    path="/openapi/*",
+    summary="OpenAPI 3.1 documentation",
+    description="OpenAPI 3.1 documentation for this resource."
+)
+@HtmlDocConfig(rank=10, navlinks={ "back: servlet:/", "json: 
servlet:/openapi?Accept=text/json&plainText=true" }, aside="NONE")
+@MarshalledConfig(swaps={ RedocUI.class })
+default OpenApi getOpenApi(RestRequest req) {
+    if (API_FORMAT_SWAGGER.equals(req.getContext().getApiFormat()))
+        throw new NotFound();
+    return req.getOpenApi().orElseThrow(NotFound::new);
+}
 ```
 
-The underlying mechanics are simple.
-The <a 
href="/site/apidocs/org/apache/juneau/rest/servlet/BasicRestServlet.html#getSwagger(org.apache.juneau.rest.RestRequest)"
 target="_blank">BasicRestServlet.getSwagger(RestRequest)</a> method returns a 
<a href="/site/apidocs/org/apache/juneau/bean/swagger/Swagger.html" 
target="_blank">Swagger</a> bean consisting of information gathered from 
annotations and other sources.
-Then that bean is swapped for a <a 
href="/site/apidocs/org/apache/juneau/bean/swagger/ui/SwaggerUI.html" 
target="_blank">SwaggerUI</a> bean when
-rendered as HTML.
+The mechanics are the same on both endpoints — the `getSwagger`/`getOpenApi` 
methods return the corresponding spec bean (built from annotations and other 
sources), and the bean is swapped for a `SwaggerUI` or `RedocUI` rendering bean 
when an HTML response is being produced.
+
+## `@Rest(apiFormat=…)` knob
+
+The new `@Rest(apiFormat=…)` attribute selects which spec format the canonical 
`/api/*` endpoint serves and whether the new `/openapi/*` sibling is mounted. 
The default is `"swagger"` (back-compat).
+
+| Value | `/api/*` | `/openapi/*` |
+|-------|----------|--------------|
+| `"swagger"` (default) | Swagger v2 + Swagger UI for `text/html` | 404 |
+| `"openapi"` | 404 | OpenAPI 3.1 + Redoc for `text/html` |
+| `"both"` | Swagger v2 + Swagger UI | OpenAPI 3.1 + Redoc |
+
+```java
+// Default behavior: Swagger v2 on /api/*; /openapi/* returns 404.
+@Rest
+public class LegacyResource extends BasicRestServlet { ... }
+
+// Modern resource: OpenAPI 3.1 on /openapi/*; /api/* returns 404.
+@Rest(apiFormat="openapi")
+public class ModernResource extends BasicRestServlet { ... }
+
+// Both endpoints active for transitional rollouts.
+@Rest(apiFormat="both")
+public class TransitionResource extends BasicRestServlet { ... }
+```
 
-Note that to have your resource create Swagger UI, you must either extend from 
one of the basic resource classes or
-provide your own <a 
href="/site/apidocs/org/apache/juneau/rest/annotation/RestOp.html" 
target="_blank">@RestOp</a>-annotated method that returns a <a 
href="/site/apidocs/org/apache/juneau/bean/swagger/Swagger.html" 
target="_blank">Swagger</a> object and a <a 
href="/site/apidocs/org/apache/juneau/bean/swagger/ui/SwaggerUI.html" 
target="_blank">SwaggerUI</a> swap.
+Resolution precedence: `@Rest(apiFormat=…)` (most-derived non-empty value 
wins) → system property `juneau.rest.apiFormat` → default `"swagger"`. Surface 
via `RestContext.getApiFormat()`. Constants live on `RestServerConstants` 
(`API_FORMAT_SWAGGER`, `API_FORMAT_OPENAPI`, `API_FORMAT_BOTH`, 
`SYSPROP_apiFormat`).
+
+To use either endpoint without extending the basic resource classes, provide 
your own `@RestGet`-annotated method that returns a <a 
href="/site/apidocs/org/apache/juneau/bean/swagger/Swagger.html" 
target="_blank">Swagger</a> or <a 
href="/site/apidocs/org/apache/juneau/bean/openapi3/OpenApi.html" 
target="_blank">OpenApi</a> bean and the matching `SwaggerUI` / `RedocUI` swap.
+
+## `components.schemas` reuse
+
+The OpenAPI 3.1 emission path collapses duplicated inline schemas into 
reusable `components.schemas` entries. Two layers contribute:
+
+- **Bean-class lift via Swagger v2 `definitions`.** `BasicRestOperations` 
carries `@JsonSchemaConfig(useBeanDefs="true")`, so when the underlying Swagger 
generator encounters a bean class it lifts the schema into 
`definitions[<beanClassName>]` and emits 
`{"$ref":"#/definitions/<beanClassName>"}` from each operation slot. The 
OpenAPI transform rewrites `definitions` to `components.schemas` and 
`#/definitions/<name>` refs to `#/components/schemas/<name>` refs.
+- **Inline dedup pass.** After the JSON-level Swagger-2.0 → OpenAPI-3.1 
transform, `BasicOpenApiProviderSession` walks every operation parameter, 
request-body, and response-content `schema` slot. Inline schemas (those without 
`$ref`) that appear two or more times are hoisted into `components.schemas` and 
each occurrence is rewritten to a `{"$ref":"#/components/schemas/<name>"}` 
pointer. The lifted entry is keyed by the schema's `title` when present and 
unique; otherwise a synthesized `Sc [...]
+
+## `?Swagger` / `?OpenApi` query mirrors on group resources
+
+`BasicGroupOperations` defines two `GET /` overloads gated by query parameter 
matchers so that group-style resources (`BasicRestServletGroup`, 
`BasicRestObjectGroup`, `BasicSpringRestServletGroup`) expose the API documents 
inline:
+
+| Query parameter | Returns |
+|-----------------|---------|
+| _(none)_ | `ChildResourceDescriptions` navigation page from 
`getChildren(RestRequest)` |
+| `?Swagger=…` (any value) | Swagger v2 document for this group resource 
(Swagger UI for `text/html`) |
+| `?OpenApi=…` (any value) | OpenAPI 3.1 document for this group resource 
(Redoc UI for `text/html`) |
+
+Each query mirror honors `@Rest(apiFormat=…)`: the `?Swagger` mirror returns 
404 when `apiFormat="openapi"`, and the `?OpenApi` mirror returns 404 when 
`apiFormat="swagger"` (the default).
+
+```java
+// Default (apiFormat="swagger"):
+// GET /            -> navigation page
+// GET /?Swagger    -> Swagger v2 doc
+// GET /?OpenApi    -> 404
+@Rest(children={MyChild.class})
+public class DefaultGroup extends BasicRestServletGroup {}
+
+// Both formats active:
+// GET /            -> navigation page
+// GET /?Swagger    -> Swagger v2 doc
+// GET /?OpenApi    -> OpenAPI 3.1 doc
+@Rest(apiFormat="both", children={MyChild.class})
+public class DualGroup extends BasicRestServletGroup {}
+```
diff --git a/pages/topics/10.20a.RestServerProblemDetails.md 
b/pages/topics/10.20a.RestServerProblemDetails.md
new file mode 100644
index 0000000000..27ea739366
--- /dev/null
+++ b/pages/topics/10.20a.RestServerProblemDetails.md
@@ -0,0 +1,328 @@
+---
+title: "RFC 7807 / 9457 Problem Details"
+slug: RestServerProblemDetails
+---
+
+Juneau REST servers can emit machine-readable [RFC 7807 — Problem Details for 
HTTP APIs](https://www.rfc-editor.org/rfc/rfc7807) responses 
(`application/problem+json`) end-to-end. The feature couples three pieces:
+
+- The typed `Problem` bean from 
[juneau-bean-rfc7807](/docs/topics/JuneauBeanRfc7807) — the wire shape.
+- An opt-in flag (`problemDetails`) on `@Rest` and every `@RestOp`-group 
annotation — turns on `application/problem+json` rendering of error responses 
for the resource (or single operation).
+- A built-in `ProblemDetailsProcessor` slotted into the default 
response-processor chain — recognises returned `Problem`s, thrown 
`ProblemException`s, thrown `BasicHttpException`s on opted-in operations, and 
any other thrown `Throwable` that a registered `ProblemMapper` chooses to 
translate.
+
+RFC 7807 was obsoleted by [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) 
in July 2023, but the data model and the `application/problem+json` IANA 
registration are unchanged. The feature is RFC 7807 / 9457-compatible by 
construction.
+
+## Motivation
+
+Out of the box, an uncaught <a 
href="/site/apidocs/org/apache/juneau/http/response/BasicHttpException.html" 
target="_blank">BasicHttpException</a> renders as a `text/plain` body 
containing the message and a status line. That is fine for browsers and quick 
`curl` debugging, but does not carry enough machine-readable detail for API 
clients (no extension fields, no typed `type`/`instance` URIs, no consistent 
shape for downstream tooling). RFC 7807 standardises that shape — five 
canonical me [...]
+
+The Juneau wiring is **opt-in per resource (or per operation)**. Existing 
resources keep their `text/plain` error path unchanged; only resources that 
declare `@Rest(problemDetails="true")` (or operations that declare 
`@RestGet(problemDetails="true")`, etc.) switch to `application/problem+json` 
for errors.
+
+## Opt-in semantics
+
+The opt-in flag is a tri-state `String`:
+
+| Value     | Effect                                                           
                                   |
+|-----------|-----------------------------------------------------------------------------------------------------|
+| `"true"`  | The resource (or operation) emits `application/problem+json` for 
errors.                            |
+| `"false"` | The resource (or operation) does not emit problem-details — 
falls back to the legacy `text/plain`.  |
+| `""`      | Inherits from the next-most-derived `@Rest`. For per-op 
annotations, inherits from `@Rest(problemDetails)`. |
+
+### Resource-level
+
+```java
+@Rest(path="/orders", problemDetails="true")
+public class OrderResource {
+
+    @RestGet("/{id}")
+    public Order get(@Path long id) {
+        throw new NotFound("Order {0} not found", id);  // → 404 
application/problem+json
+    }
+}
+```
+
+### Per-operation fanout
+
+The same attribute is available on every method-level annotation: `@RestGet`, 
`@RestPost`, `@RestPut`, `@RestPatch`, `@RestDelete`, `@RestOptions`, 
`@RestOp`. Per-op `"true"` opts an op in on a non-opted-in resource; per-op 
`"false"` opts an op out of an opted-in resource. `""` inherits.
+
+```java
+@Rest(path="/api", problemDetails="false")
+public class ApiResource {
+
+    @RestGet("/legacy")            // text/plain on error (inherits "false")
+    public String legacy() { ... }
+
+    @RestGet(path="/orders/{id}", problemDetails="true")  // 
application/problem+json on error
+    public Order getOrder(@Path long id) {
+        throw new NotFound("Order {0} not found", id);
+    }
+}
+```
+
+### `Accept` negotiation
+
+The processor follows the spec's two-track policy:
+
+- **Error path** — `application/problem+json` is emitted regardless of the 
client `Accept` header. RFC 7807 §3 encourages this: the alternative leaves the 
client with a 4xx/5xx and a `text/plain` stack trace, which is strictly worse 
than a typed error body.
+- **Success path** — the client `Accept` header is honored strictly. If a 
`@RestGet` method *returns* a `Problem` (or `ProblemException`), the processor 
only emits `application/problem+json` when the client's `Accept` matches that 
media type (or `*/*`); otherwise the processor passes through to the next 
processor in the chain unchanged.
+
+## Return paths
+
+There are three ways a Juneau handler can produce a `Problem`-shaped response:
+
+### 1. Return a `Problem` directly
+
+The most explicit path — opts in on the call site, regardless of 
`@Rest(problemDetails)`:
+
+```java
+@RestPost
+public Problem create(Order in) {
+    if (in.balance < in.amount)
+        return Problem.fromStatus(403, "Insufficient credit", "Balance " + 
in.balance + " < amount " + in.amount)
+            .setType(URI.create("https://example.com/probs/out-of-credit";))
+            .set("balance", in.balance);
+    return null;
+}
+```
+
+This works regardless of the `problemDetails` opt-in flag — returning a 
`Problem` is itself the opt-in signal. The flag only controls error-path 
(thrown-exception) behavior.
+
+### 2. Throw a `ProblemException`
+
+For the throw idiom, `juneau-bean-rfc7807` ships a small <a 
href="/site/apidocs/org/apache/juneau/bean/rfc7807/ProblemException.html" 
target="_blank">ProblemException</a> `RuntimeException` that carries a 
`Problem`:
+
+```java
+@RestPost
+public Receipt buy(Order in) {
+    if (in.balance < in.amount) {
+        throw new ProblemException(
+            Problem.fromStatus(403, "Insufficient credit", "Balance " + 
in.balance + " < amount " + in.amount)
+                .setType(URI.create("https://example.com/probs/out-of-credit";))
+                .set("balance", in.balance)
+                .set("accounts", List.of("/account/12345", "/account/67890")));
+    }
+    return chargeAccount(in);
+}
+```
+
+On opted-in operations the processor unwraps the `ProblemException`, emits 
`application/problem+json`, and ignores the client `Accept` (per the error-path 
policy). On non-opted-in operations the processor honors `Accept` strictly — 
useful for passing a `Problem`-shaped success response through.
+
+### 3. Throw a `BasicHttpException` (most common)
+
+The simplest path: opt the resource in and keep throwing the regular 
`BasicHttpException` subclasses. The processor adapts the exception into a 
`Problem` via the default `ProblemAdapters.fromException(...)` helper (status 
code, reason phrase, `getMessage()`):
+
+```java
+@Rest(problemDetails="true")
+public class OrderResource {
+
+    @RestGet("/{id}")
+    public Order get(@Path long id) {
+        throw new NotFound("Order {0} not found", id);
+    }
+}
+```
+
+Wire response:
+
+```http
+HTTP/1.1 404 Not Found
+Content-Type: application/problem+json
+
+{
+    "detail": "Order 42 not found",
+    "status": 404,
+    "title": "Not Found"
+}
+```
+
+For finer control over the emitted `Problem` (custom `type` URI, extension 
fields, locale-aware `title` / `detail`), register a `ProblemMapper` — see 
below.
+
+## The `Problem` shape
+
+See [juneau-bean-rfc7807](/docs/topics/JuneauBeanRfc7807) for the bean 
reference. In summary:
+
+| Property   | Type      | Notes                                               
                               |
+|------------|-----------|------------------------------------------------------------------------------------|
+| `type`     | `URI`     | Nullable. Omitted from the wire when unset (no 
synthetic `"about:blank"`).         |
+| `title`    | `String`  | Short, human-readable summary of the problem type.  
                               |
+| `status`   | `Integer` | OPTIONAL HTTP status code; serialised as a JSON 
number when set, omitted when `null`. |
+| `detail`   | `String`  | Human-readable explanation specific to this 
occurrence.                            |
+| `instance` | `URI`     | URI identifying the specific occurrence.            
                               |
+| (extras)   | varies    | Arbitrary §3.2 extension members; serialise flat at 
the top level.                 |
+
+When `Problem.status` is non-`null`, the processor calls 
`RestResponse.setStatus(int)` with that value. Otherwise it leaves the existing 
response status alone (`RestSession.run()` normalises `0` → `200`, so per-op 
default codes such as `@RestPost`'s default of 200 still flow through 
unchanged).
+
+## `ProblemMapper` — declarative exception translation
+
+For richer error shaping, register a <a 
href="/site/apidocs/org/apache/juneau/bean/rfc7807/ProblemMapper.html" 
target="_blank">ProblemMapper</a> bean on the resource. The SPI is two methods:
+
+```java
+public interface ProblemMapper<T extends Throwable> {
+    Class<T> getExceptionType();
+    Problem map(T exception);
+}
+```
+
+The processor discovers all `ProblemMapper`s reachable from the resource bean 
store, picks the **most-specific** mapper (the one whose `getExceptionType()` 
sits closest to the thrown class in the hierarchy), and uses its `Problem` 
return value. A mapper that returns `null` is treated as "no opinion" — the 
processor falls through to the next-most-specific mapper, and ultimately to the 
built-in `ProblemAdapters.fromException(...)` fallback for `BasicHttpException` 
subclasses.
+
+### Single mapper
+
+```java
+public class InsufficientCreditException extends RuntimeException {
+    private final int balance;
+    private final int cost;
+
+    public InsufficientCreditException(int balance, int cost) {
+        super("Balance " + balance + " < cost " + cost);
+        this.balance = balance;
+        this.cost = cost;
+    }
+
+    public int getBalance() { return balance; }
+    public int getCost() { return cost; }
+}
+
+public class InsufficientCreditMapper implements 
ProblemMapper<InsufficientCreditException> {
+    public Class<InsufficientCreditException> getExceptionType() { return 
InsufficientCreditException.class; }
+    public Problem map(InsufficientCreditException e) {
+        return Problem.fromStatus(403, "Insufficient credit", e.getMessage())
+            .setType(URI.create("https://example.com/probs/out-of-credit";))
+            .setInstance(URI.create("/account/12345/msgs/abc"))
+            .set("balance", e.getBalance())
+            .set("cost", e.getCost());
+    }
+}
+
+@Rest(problemDetails="true")
+public class AccountResource {
+
+    @Bean public ProblemMapper<InsufficientCreditException> creditMapper() {
+        return new InsufficientCreditMapper();
+    }
+
+    @RestGet("/buy")
+    public Receipt buy() {
+        throw new InsufficientCreditException(30, 50);  // translated by 
creditMapper()
+    }
+}
+```
+
+Wire response:
+
+```http
+HTTP/1.1 403 Forbidden
+Content-Type: application/problem+json
+
+{
+    "balance": 30,
+    "cost": 50,
+    "detail": "Balance 30 < cost 50",
+    "instance": "/account/12345/msgs/abc",
+    "status": 403,
+    "title": "Insufficient credit",
+    "type": "https://example.com/probs/out-of-credit";
+}
+```
+
+### Multiple mappers — use `ProblemMapperList`
+
+The Juneau `@Bean` bean-store walk pairs each `@Bean` factory method with its 
declared return type, so multiple `@Bean public ProblemMapper foo()` factories 
on the same resource class collapse onto the single `ProblemMapper.class` slot 
(and only one of them survives in the bean store). To register more than one 
mapper, wrap them in a <a 
href="/site/apidocs/org/apache/juneau/bean/rfc7807/ProblemMapperList.html" 
target="_blank">ProblemMapperList</a> — a single bean that carries the ordered 
[...]
+
+```java
+@Rest(problemDetails="true")
+public class CheckoutResource {
+
+    @Bean public ProblemMapperList problemMappers() {
+        return ProblemMapperList.of(
+            new InsufficientCreditMapper(),
+            new OrderNotFoundMapper(),
+            new GenericHttpErrorMapper()  // broad fallback — runs last by 
hierarchy depth
+        );
+    }
+
+    @RestGet("/buy")
+    public Receipt buy() { throw new InsufficientCreditException(30, 50); }
+}
+```
+
+The processor sorts the list by exception-class hierarchy depth and tries each 
matching mapper in most-specific-first order, skipping any mapper that returns 
`null`. Bean-registration order serves as the tiebreaker for mappers at the 
same depth.
+
+### Default fallback
+
+When no `ProblemMapper` matches (or every matching mapper returns `null`) and 
the thrown exception is a `BasicHttpException`, the processor calls <a 
href="/site/apidocs/org/apache/juneau/bean/rfc7807/adapter/ProblemAdapters.html"
 target="_blank">ProblemAdapters.fromException(BasicHttpException)</a> as the 
built-in fallback. This is the lowest-friction path — opting a resource in with 
`@Rest(problemDetails="true")` and throwing the regular `BasicHttpException` 
subclasses (`NotFound`, `Bad [...]
+
+## Localization — future-work seam
+
+The processor consults the resource bean store for a <a 
href="/site/apidocs/org/apache/juneau/bean/rfc7807/ProblemLocalizationStrategy.html"
 target="_blank">ProblemLocalizationStrategy</a> bean before emitting the 
`Problem`, passing the resolved `Problem` and the negotiated request locale 
through it. The default behavior is `ProblemLocalizationStrategy.IDENTITY` — a 
pass-through that returns the input `Problem` unchanged.
+
+```java
+@FunctionalInterface
+public interface ProblemLocalizationStrategy {
+    Problem localize(Problem problem, Locale locale);
+}
+```
+
+This is a deliberate **future-work seam**, not a finished feature. The 
reference `Messages`-driven (resource-bundle) translation pass is intentionally 
out of scope for this release; the `IDENTITY` default keeps the call-site 
contract stable so a future implementation can be dropped in without changing 
the processor.
+
+Users with custom localization needs can already register a strategy bean 
today:
+
+```java
+@Bean public ProblemLocalizationStrategy localize() {
+    return (problem, locale) -> {
+        var bundle = ResourceBundle.getBundle("problems", locale == null ? 
Locale.ROOT : locale);
+        var typeKey = String.valueOf(problem.getType());
+        if (bundle.containsKey(typeKey + ".title"))
+            problem.setTitle(bundle.getString(typeKey + ".title"));
+        if (bundle.containsKey(typeKey + ".detail"))
+            problem.setDetail(bundle.getString(typeKey + ".detail"));
+        return problem;
+    };
+}
+```
+
+A `null` strategy return is treated as "no opinion" and the pre-localization 
`Problem` is used instead.
+
+## Worked example — end-to-end
+
+```java
+@Rest(path="/orders", problemDetails="true")
+public class OrderResource {
+
+    @Bean public ProblemMapperList problemMappers() {
+        return ProblemMapperList.of(new InsufficientCreditMapper());
+    }
+
+    @RestGet("/{id}")
+    public Order get(@Path long id) {
+        // Throws NotFound → adapted via the default ProblemAdapters fallback.
+        return Optional.ofNullable(repo.find(id))
+            .orElseThrow(() -> new NotFound("Order {0} not found", id));
+    }
+
+    @RestPost
+    public Receipt create(Order in) {
+        // Throws InsufficientCreditException → translated by the registered 
ProblemMapper.
+        return checkout.buy(in);
+    }
+
+    @RestDelete(path="/{id}", problemDetails="false")
+    public void cancel(@Path long id) {
+        // Per-op opt-out: cancellation errors fall back to legacy text/plain.
+        repo.delete(id);
+    }
+}
+```
+
+The two `GET` and `POST` operations emit `application/problem+json` on error; 
the `DELETE` operation opts out per-op and falls back to `text/plain`.
+
+## See also
+
+- [juneau-bean-rfc7807](/docs/topics/JuneauBeanRfc7807) — the `Problem` bean 
reference.
+- [Java Method Throwable Types](/docs/topics/JavaMethodThrowableTypes) — what 
exceptions Juneau handlers can throw and how they map to HTTP statuses.
+- [HTTP Status Codes](/docs/topics/HttpStatusCodes) — the framework defaults 
for non-OK statuses.
+- [Response Processors](/docs/topics/ResponseProcessors) — the processor-chain 
mechanism `ProblemDetailsProcessor` slots into.
+
+## Resources
+
+- [RFC 7807 — Problem Details for HTTP 
APIs](https://www.rfc-editor.org/rfc/rfc7807)
+- [RFC 9457 — Problem Details for HTTP APIs (obsoletes 
7807)](https://www.rfc-editor.org/rfc/rfc9457)
+- [IANA `application/problem+json` 
registration](https://www.iana.org/assignments/media-types/application/problem+json)
diff --git a/pages/topics/23.01.V9.5-migration-guide.md 
b/pages/topics/23.01.V9.5-migration-guide.md
index 0ca4fe9ecb..1eae1d6893 100644
--- a/pages/topics/23.01.V9.5-migration-guide.md
+++ b/pages/topics/23.01.V9.5-migration-guide.md
@@ -120,6 +120,24 @@ public class MyType {
 }
 ```
 
+### Removed `@MarshalledProp(properties)` Attribute
+
+The `@MarshalledProp(properties=...)` attribute has been removed without a 
deprecation shim. The attribute was used to limit which child properties of a 
nested bean / map are rendered by serializers.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `@MarshalledProp(properties="f1") public MyChildClass x1` — only `f1` 
rendered on the nested child | Drop the attribute (nested beans render in 
full); for per-property filtering apply `@Marshalled(properties=...)` at the 
**child class** level, or configure include / exclude lists on the 
`MarshallingContext.Builder` | Hard removal. The marshalling-context-level 
filtering pre-dated `@MarshalledProp(properties)` and remains fully supported. |
+| `BeanPropertyMeta.applyChildPropertiesFilter(...)` / 
`BeanPropertyMeta.getProperties()` (override-list accessor) and the underlying 
`properties` field/builder setter | Removed | Internal types — no replacement. |
+| `org.apache.juneau.BeanMetaFiltered` | Removed | Sole purpose was wrapping a 
`BeanMeta` with a filtered property list; no replacement needed. |
+
+### `@Beanp("*")` on Non-Map Fields
+
+On a field whose type is **not** a `Map`, `@Beanp("*")` (or 
`@Beanp(name="*")`) no longer attempts to register a dyna property. The 
property name is now taken from the field via the configured `PropertyNamer`; 
other `@Beanp` attributes still apply. `Map` fields keep the dyna-property `*` 
behavior as before.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `@Beanp("*") public String myField;` registered a no-op dyna property keyed 
on `*` | Field is named via the configured `PropertyNamer` (typically 
`myField`); `*` is treated as if absent | Behavioral. Affects any non-`Map` 
field that was annotated with `@Beanp("*")`. Replace with `@Beanp` (no name) to 
keep the namer-derived name, or `@Beanp(name="myField")` to pin it explicitly. |
+
 ## Bean-Modeling Layer Split
 
 The bean-modeling layer has been split out of `juneau-marshall` into 
`juneau-commons` (Phase 5 of the bean-layer split). The seven core bean-runtime 
types and the `@Name` annotation have moved into 
`org.apache.juneau.commons.bean`, and a new set of bean-modeling annotations 
and SPI types has been added there. `juneau-commons` now compiles standalone 
with no dependency on `juneau-marshall`.
@@ -450,5 +468,216 @@ Closes out the format-control extension work (TODO-4 / 
TODO-50 / TODO-52 / TODO-
 | `BinaryFormat.BASE64_URL.parse("mQ")` (or any non-3-aligned URL-safe payload 
without `-` / `_` chars) threw `IllegalArgumentException: Invalid BASE64 string 
length` | Decodes correctly via `Base64.getUrlDecoder()` (which accepts missing 
padding) | **Bug fix, not a breaking change.** `BinaryFormat.parse` now honors 
the `BASE64_URL` constant directly before falling through to the 
format-agnostic wire-shape sniff. No source change required. |
 | `JsonParser.DEFAULT.parse("3.14", Object.class)` returned `java.lang.Float` 
*(or any JSON-family parser auto-classifying a bare decimal)* | Returns 
`java.lang.Double` | **Behavioral change (Bug #5 closure)** — see release notes 
for the full rationale. The shared classifier `StringUtils.parseNumber` no 
longer auto-compacts to `Float` when the lossless `Float`/`Double` `toString()` 
representations happen to match. Affects all JSON-family parsers (JSON / JSON5 
/ JSONL / XML / HTML / UON / [...]
 
-<!-- Additional rows will be populated as 9.5 breaking changes land. See 
todo/TODO-17 for the
-ongoing 9.5.0 audit. -->
+## Format-Control: Duration / Period Defaults (TODO-4)
+
+New `org.apache.juneau.DurationFormat` and `org.apache.juneau.PeriodFormat` 
enums plus `MarshallingContext.Builder.durationFormat(...)` / 
`periodFormat(...)` settings, mirrored on the three Marshalled annotations 
(`@Marshalled` / `@MarshalledProp` / `@MarshalledConfig`). The default 
`Duration` wire shape changed.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| Default `Duration` serialization → ISO 8601 hours/minutes form 
(`Duration.ofHours(48)` → `'PT48H'`) | **Default is now 
`DurationFormat.ISO_8601_WITH_DAYS`** (`Duration.ofHours(48)` → `'P2D'`) | 
**Behavioral default change.** To restore the pre-9.5 wire shape, configure 
`MarshallingContext.Builder.durationFormat(DurationFormat.ISO_8601)` (or 
`@MarshalledProp(durationFormat=DurationFormat.ISO_8601)` per-property). The 
`DurationFormat` constants are `ISO_8601`, `ISO_8601_WITH_DAYS`, `NANO [...]
+| n/a | New `MarshallingContext.Builder.periodFormat(PeriodFormat)` (default 
`ISO_8601`; also `DAYS`) | Additive. Mirrored on the three Marshalled 
annotations. |
+| Binary serializers (BSON / CBOR / MsgPack) wrote `Duration` / `Period` as 
strings | Emit native numeric wire types when `durationFormat` / `periodFormat` 
resolves to a numeric constant (`NANOS` / `MILLIS` / `SECONDS`) | 
Source-compatible; wire shape narrows when configured to numeric. |
+
+## Format-Control: Calendar / Date / Temporal / TimeZone / Locale Swap 
Deletions (TODO-51)
+
+The legacy `Temporal*Swap` inner-class family and the named root-level 
temporal / locale / timezone swaps have been removed **without deprecation 
shims**. Configure formats via `MarshallingContext` setters or the new 
annotations instead. New enums `CalendarFormat`, `DateFormat`, 
`TemporalFormat`, `TimeZoneFormat`, `LocaleFormat` live under 
`org.apache.juneau`.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `org.apache.juneau.swaps.TemporalCalendarSwap` and all 17 inner-class 
variants (`IsoOffsetDateTime`, `IsoInstant`, `IsoLocalDateTime`, 
`IsoZonedDateTime`, etc.) | 
`@MarshalledProp(calendarFormat=CalendarFormat.ISO_XXX)` / 
`@Marshalled(calendarFormat=...)` / 
`MarshallingContext.Builder.calendarFormat(CalendarFormat.ISO_XXX)` | Hard 
removal — no `@Deprecated` shim. Replace 
`@Swap(TemporalCalendarSwap.IsoYyy.class)` with 
`@MarshalledProp(calendarFormat=CalendarFormat.ISO_YYY)`. For builde [...]
+| `org.apache.juneau.swaps.TemporalDateSwap` and all 17 inner-class variants | 
`@MarshalledProp(dateFormat=DateFormat.ISO_XXX)` / 
`@Marshalled(dateFormat=...)` / 
`MarshallingContext.Builder.dateFormat(DateFormat.ISO_XXX)` | Hard removal. 
Same migration shape. `DateFormat` constants: ISO variants, 
`RFC_1123_DATE_TIME`, `MILLIS` (default `ISO_LOCAL_DATE_TIME`). |
+| `org.apache.juneau.swaps.TemporalSwap` and all 18 inner-class variants | 
`@MarshalledProp(temporalFormat=TemporalFormat.ISO_XXX)` / 
`@Marshalled(temporalFormat=...)` / 
`MarshallingContext.Builder.temporalFormat(TemporalFormat.ISO_XXX)` | Hard 
removal. `TemporalFormat` constants: `DEFAULT` (per-subtype default), ISO 
variants, `RFC_1123_DATE_TIME`, `ISO_YEAR`, `ISO_YEAR_MONTH`, `MILLIS`. |
+| `org.apache.juneau.swaps.XMLGregorianCalendarSwap` | Built-in — 
`XMLGregorianCalendar` properties are always XML-lexical regardless of any 
configured `CalendarFormat`. For `Calendar` / `GregorianCalendar` use 
`CalendarFormat.XML_FORMAT` as the opt-in. | The always-XML behavior moved into 
the post-processor for `XMLGregorianCalendar`; no annotation needed. |
+| `swaps(TimeZoneSwap.class)` / `swaps(ZoneIdSwap.class)` on builders, used to 
control root-level `TimeZone` / `ZoneId` wire shape | 
`MarshallingContext.Builder.timeZoneFormat(TimeZoneFormat)` (default `ID`; also 
`OFFSET`, `NAME_LONG`, `NAME_SHORT`) shared by both `TimeZone` and `ZoneId` | 
The `TimeZoneSwap` / `ZoneIdSwap` types still exist but are now thin delegators 
that read the resolved `TimeZoneFormat` from the active session. |
+| `swaps(LocaleSwap.class)` for root-level `Locale` wire shape | 
`MarshallingContext.Builder.localeFormat(LocaleFormat)` (default `BCP_47`; also 
`UNDERSCORE`) | `LocaleSwap` is now a thin delegator. |
+
+**Precedence:** `@MarshalledProp` > `@Marshalled` > `MarshallingContext` 
setting > enum default.
+
+## Format-Control: Binary + Enum Swap and Builder Deletions (TODO-52)
+
+The legacy `ByteArraySwap` family, the `OutputStreamSerializer.Builder` / 
`InputStreamParser.Builder` `binaryFormat(...)` setters, and the 
`useEnumNames(boolean)` setter have been removed **without deprecation shims**. 
Configure these via `MarshallingContext.Builder` or the new annotations 
instead. New enum `org.apache.juneau.EnumFormat` plus extended 
`org.apache.juneau.BinaryFormat`.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `org.apache.juneau.swaps.ByteArraySwap` and the `Base64` / `Hex` / 
`SpacedHex` inner-class variants | 
`@MarshalledProp(binaryFormat=BinaryFormat.BASE64 / HEX / SPACED_HEX)` / 
`@Marshalled(binaryFormat=...)` / 
`MarshallingContext.Builder.binaryFormat(...)` | Hard removal. Replace 
`@Swap(ByteArraySwap.Base64.class)` with 
`@MarshalledProp(binaryFormat=BinaryFormat.BASE64)`. New `BinaryFormat.NOT_SET` 
(default, falls through to surrounding session's native shape) and 
`BinaryFormat.BASE64_U [...]
+| `OutputStreamSerializer.Builder.binaryFormat(BinaryFormat)` and 
`InputStreamParser.Builder.binaryFormat(BinaryFormat)` | 
`MarshallingContext.Builder.binaryFormat(BinaryFormat)` (inherited by every 
serializer / parser builder via `MarshallingContextable.Builder`) | The setting 
now drives both binary and textual sessions. The inherited setter has the same 
signature, so most builder chains compile unchanged. |
+| `BeanContext.Builder.useEnumNames(boolean)` and the `useEnumNames` field on 
`MarshallingContext` | `MarshallingContext.Builder.enumFormat(EnumFormat.NAME)` 
| Hard removal. `useEnumNames()` / `useEnumNames(true)` → 
`enumFormat(EnumFormat.NAME)`. Other `EnumFormat` constants: `TO_STRING` 
(default), `LOWER_HYPHEN`, `UPPER_HYPHEN`, `LOWER_UNDERSCORE`, `LOWER`, 
`UPPER`, `ORDINAL`, `NOT_SET`. |
+| `@Bean(useEnumNames=true)` | `@Marshalled(enumFormat=EnumFormat.NAME)` | 
Annotation-attribute rename. |
+| `org.apache.juneau.csv.ByteArrayFormat` | 
`org.apache.juneau.csv.CsvByteArrayCellFormat` | Rename to disambiguate from 
the new top-level `BinaryFormat`. |
+| `OutputStreamSerializerSession#serializeToString(Object)` honored the 
configured `binaryFormat` for debug output | Always emits `BinaryFormat.HEX` | 
Source-compatible; behavioral. Provides a stable copy-pasteable hex dump for 
debug / display irrespective of the surrounding context's wire-format setting. |
+
+## Format-Control: `ClassSwap` Deletion + Boolean / Float / Currency / Class 
Settings (TODO-54)
+
+`ClassSwap` is deleted; bean-property `Class<?>` serialization is driven by 
`@MarshalledProp(classFormat=...)` (with `ClassFormat.FQCN` as the default). 
New paired enums `BooleanFormat`, `FloatFormat`, `CurrencyFormat`, 
`ClassFormat` plus `MarshallingContext.Builder` setters and matching 
`@Marshalled(...)` / `@MarshalledProp(...)` / `@MarshalledConfig(...)` 
attributes.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `org.apache.juneau.swaps.ClassSwap` | 
`@MarshalledProp(classFormat=ClassFormat.FQCN)` / 
`@Marshalled(classFormat=...)` / 
`MarshallingContext.Builder.classFormat(ClassFormat)` for bean properties; for 
top-level `Class<?>` values the new `org.apache.juneau.swaps.ClassFormatSwap` 
is registered in `DefaultSwaps` and reads the resolved `ClassFormat` at swap 
time | Hard deletion. Default `ClassFormat.FQCN` (≈ `Class.getCanonicalName()`) 
is functionally compatible with the old `ClassSwap.getN [...]
+| `MarshallingContext` had no top-level `booleanFormat` / `floatFormat` / 
`currencyFormat` / `classFormat` setting | New 
`MarshallingContext.Builder.booleanFormat(BooleanFormat)` / 
`floatFormat(FloatFormat)` / `currencyFormat(CurrencyFormat)` / 
`classFormat(ClassFormat)` settings | Additive. **Defaults:** 
`BooleanFormat.TRUE_FALSE`, `FloatFormat.NaN_AS_NULL` (boxed `Float` / `Double` 
only — primitive `float` / `double` keep the legacy null-to-zero contract), 
`CurrencyFormat.ISO_CODE`, `C [...]
+| `Currency` bean properties at the default `CurrencyFormat` were unrecognized 
(no default swap) | `Currency` round-trips via `DefaultSwaps` registration at 
`CurrencyFormat.ISO_CODE` / `NOT_SET` | Bug #6 closure; source-compatible. |
+
+## JSON Strict-Mode Separation (`JsonParser` / `JsonSerializer` vs 
`Json5Parser` / `Json5Serializer`)
+
+`JsonSerializer` / `JsonParser` now strictly enforce RFC 8259 (double-quoted 
strings only). Single-quoted JSON, comments, trailing commas, and unquoted keys 
are rejected by `JsonParser` and no longer produced by `JsonSerializer`. JSON5 
input / output must go through `Json5Parser` / `Json5Serializer`.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `JsonSerializer.create().json5().build()` — JSON5 output via the strict 
serializer | Use `Json5Serializer.create()....build()` directly | Hard removal 
of `JsonSerializer.json5()`. No deprecation shim. |
+| `JsonParser.Strict` static instance / `Strict` inner subclass | Use 
`JsonParser.DEFAULT` (now strict) for JSON; `Json5Parser.DEFAULT` for JSON5 | 
Hard removal. No deprecation shim. |
+| `JsonParser.DEFAULT.parse("{key:'val'}", ...)` accepted JSON5 (unquoted 
keys, single quotes) | Throws `ParseException`. Use `Json5Parser.DEFAULT` for 
JSON5 input. | **Hard behavioral break.** Callers passing JSON5 strings to the 
default JSON parser will fail at runtime. |
+| `JsonSerializer.DEFAULT.serialize(bean)` produced strict JSON (was 
historically inconsistent depending on builder flags) | Always strict JSON 
(double-quoted keys / strings, no trailing commas, no comments) | Behavioral. 
For JSON5 output use `Json5Serializer.DEFAULT.serialize(...)`. |
+| `BeanContext.beanToStringSerializer` used `JsonSerializer` for the 
bean-to-string fallback | Uses `Json5Serializer` (more human-readable). | 
Source-compatible — affects only the `toString()` shape of `BeanMap` and 
related types. |
+| `ComboRoundTrip_Tester` used `json()` for both strict-JSON and JSON5 outputs 
| Use `json()` for strict JSON, `json5()` for JSON5 — they are now distinct 
configurations | Test-only impact. |
+
+## Sorted Bean Properties by Default
+
+Bean properties are now serialized in **alphabetical order by default** across 
all serializers. Previously the default was natural JVM order and opting in to 
sorted output required explicit configuration. The `sortProperties` API is 
fully removed and replaced by `unsortedProperties` with inverted semantics.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `@Bean(sort=true)` — opt-in to sorted output | `@Bean(unsorted=true)` — 
opt-out of default sorted output | Hard rename with inverted semantics. 
Removing both attributes leaves properties sorted (was: unsorted). |
+| `BeanContext.Builder.sortProperties()` / `sortProperties(boolean)` | 
`BeanContext.Builder.unsortedProperties()` / `unsortedProperties(boolean)` | 
Hard removal. Inverted semantics. |
+| `BeanContext.Builder.sortProperties(Class<?>...on)` | 
`BeanContext.Builder.unsortedProperties(Class<?>...on)` | Same. |
+| `BeanContextable.Builder.sortProperties()` / `sortProperties(Class<?>...on)` 
| `unsortedProperties()` / `unsortedProperties(Class<?>...on)` | Mirrored on 
every serializer / parser builder. |
+| `BeanFilter.Builder.sortProperties()` / `sortProperties(boolean)` | 
`BeanFilter.Builder.unsortedProperties()` | Per-bean-filter opt-out. |
+| `BeanFilter.isSortProperties()` / `BeanSession.isSortProperties()` / 
`BeanMeta.isSortProperties()` | `BeanFilter.isUnsortedProperties()` / 
`BeanSession.isUnsortedProperties()` / (internal field renamed on `BeanMeta`) | 
Inverted semantics. |
+| All ~55 serializer / parser builder `sortProperties()` / 
`sortProperties(Class<?>...on)` overrides (JSON, XML, HTML, YAML, UON, URL 
Encoding, CSV, OpenAPI, MsgPack, CBOR, BSON, RDF, etc.) | All replaced by 
`unsortedProperties()` / `unsortedProperties(Class<?>...on)` overrides | Hard 
rename across the entire builder hierarchy. |
+| Static `DEFAULT_SORTED` constants on `JsonSerializer` etc. | Removed | 
Redundant — `DEFAULT` is now sorted. |
+
+## Native `Iterator` / `Iterable` / `Stream` / `Enumeration` Serialization
+
+`Iterator`, non-`Collection` `Iterable`, `Enumeration`, and 
`java.util.stream.Stream` are now serialized directly as arrays without 
`IteratorSwap` / `EnumerationSwap` materializing them to a `LinkedList` first. 
JSON / XML / UON / URL Encoding / OpenAPI write elements lazily; MsgPack / HTML 
/ CSV collect to a `List` internally because the format requires up-front 
knowledge of size or column headers.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `org.apache.juneau.swaps.IteratorSwap` | Removed. `Iterator` handled 
natively. | If you registered this swap explicitly, drop the registration. |
+| `org.apache.juneau.swaps.EnumerationSwap` | Removed. `Enumeration` handled 
natively. | Same. |
+| `Stream<T>` and non-`Collection` `Iterable<T>` had no serialization support 
— fell through to bean serialization (or threw) | Serialized directly as 
arrays. `Stream<T>` is auto-closed via try-with-resources after iteration. | 
Additive new behavior. |
+| `Supplier<T>` (standard JDK, not `BeanSupplier`) inside a bean property 
serialized as a bean wrapping the supplier itself | Transparently unwrapped by 
serializers as a single lazy value, recursively up to depth 10. | Behavioral. 
Affects any place a `Supplier<T>` is exposed as a bean property. |
+| `ClassMeta` had no `isIterator()` / `isIterable()` / `isStream()` / 
`isStreamable()` predicates | New predicates plus new `Category` enum values 
`ITERATOR`, `ITERABLE`, `STREAM` | New API. POJO categories table moves these 
out of "swapped objects" (group 4b) into a new sequence-types group alongside 
Collections / arrays. |
+
+## HTTP Annotation Moves and Attribute Removals
+
+The HTTP parameter annotations (`@Header`, `@Query`, `@Path`, `@Content`, 
`@FormData`, `@Request`, `@Response`, `@StatusCode`, `@PathRemainder`, 
`@HasQuery`, `@HasFormData`, `@Contact`, `@License`, `@Tag`) along with the 
constants (`CollectionFormatType`, `FormatType`, `ParameterType`) and the 
`httppart.bean` package (`RequestBeanMeta`, `RequestBeanPropertyMeta`, 
`ResponseBeanMeta`, `ResponseBeanPropertyMeta`, `MethodInfoUtils`) have 
**moved** from `juneau-marshall` into `juneau-rest-com [...]
+
+Several attributes on these annotations have been **removed without 
deprecation shims**:
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `@Query(serializer=..., parser=...)` / `@Header(serializer=..., parser=...)` 
/ `@FormData(serializer=..., parser=...)` / `@Path(serializer=..., parser=...)` 
/ `@PathRemainder(serializer=..., parser=...)` / `@Request(serializer=..., 
parser=...)` / `@Response(serializer=..., parser=...)` | New 
`@HttpPartMarshalling(serializer=..., parser=...)` annotation (in 
`juneau-marshall`, package `org.apache.juneau.httppart`) applied alongside the 
HTTP annotation | Hard removal. The HTTP annotations [...]
+| `@Repeatable` on every HTTP annotation (`@Query`, `@Header`, `@FormData`, 
`@Path`, `@Content`, `@PathRemainder`, `@Request`, `@Response`, `@StatusCode`, 
`@HasQuery`, `@HasFormData`) | Not repeatable — author one annotation per 
element | Hard removal. Dynamic annotation stacking is no longer supported. |
+| `on()` / `onClass()` attributes on all HTTP annotations | Removed — apply 
the annotation directly to the target rather than via `on()` / `onClass()` 
proxies | Hard removal. |
+| `@ContextApply` on HTTP annotations | Removed — the annotation no longer 
participates in the context-apply pass | Hard removal. |
+| 10 `XAnnotation` companion classes: `ContentAnnotation`, 
`FormDataAnnotation`, `HasFormDataAnnotation`, `HasQueryAnnotation`, 
`HeaderAnnotation`, `PathAnnotation`, `PathRemainderAnnotation`, 
`QueryAnnotation`, `RequestAnnotation`, `StatusCodeAnnotation` | Removed (used 
by the deleted `@ContextApply` plumbing) | Hard removal. `ContactAnnotation`, 
`LicenseAnnotation`, `TagAnnotation`, and `ResponseAnnotation` remain (still 
consumed by Swagger generation utilities). |
+| `org.apache.juneau.ng.http.remote.Body` / `Header` / `Path` / `Query` *(9.5 
early snapshot only)* | `@org.apache.juneau.http.annotation.Content` / `Header` 
/ `Path` / `Query` | The NG duplicate annotations were deleted; the canonical 
`RestClient` (formerly `NgRestClient`) uses the standard annotations. |
+
+## Request Attributes vs Session Properties Separation
+
+Request attributes and parser / serializer session properties are now 
completely separate concepts. Previously, `defaultRequestAttributes` from 
`@Rest` / `@RestOp` were automatically merged into session properties, 
conflating two distinct concerns.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `@Rest(defaultRequestAttributes={"key: value"})` flowed automatically into 
the parser / serializer session for every request | `defaultRequestAttributes` 
populates only `RequestAttributes` (accessed via 
`RestRequest.getAttributes()`). Session properties must be set explicitly via 
the new programmatic API on `RestRequest`. | **Behavioral break.** Resource 
code that relied on the implicit merge must explicitly bridge it — see the rows 
below. |
+| n/a | `RestRequest.setSerializerSessionProperty(String, Object)` / 
`setParserSessionProperty(String, Object)` | Set per-request session properties 
programmatically. Typically called from `@RestPreCall` / `@RestPostCall` or 
inside the REST method before parsing / serialization. |
+| n/a | `RestRequest.setSerializerSessionProperties(Map<String,Object>)` / 
`setParserSessionProperties(Map<String,Object>)` | Set multiple session 
properties at once. Use 
`req.setSerializerSessionProperties(req.getAttributes().asMap())` to restore 
the pre-9.5 implicit-merge behavior. |
+
+## REST Session-Option Allowlist Refactor
+
+The standalone session-option allowlist machinery has been replaced by a 
`noInherit` attribute on the existing `@Rest` / `@RestOp` group of annotations. 
Programmatic `RestContext.Builder` / `RestOpContext.Builder` allowlist setters 
are gone (the builders themselves are gone — see the top of this file).
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `@NoInherit` standalone annotation | `noInherit` attribute on `@Rest` / 
`@RestOp` / `@RestGet` / `@RestPost` / `@RestPut` / `@RestPatch` / 
`@RestDelete` / `@RestOptions` | Hard removal. Use the annotation attribute 
directly, e.g. `@RestGet(allowedSerializerOptions="myOpt", 
noInherit="allowedSerializerOptions")`. Supported names include 
`allowedParserOptions` and `allowedSerializerOptions` (case-insensitive; SVL 
and comma-separated tokens expand). |
+| `org.apache.juneau.rest.util.StringTokenSet` (REST-server allowlist driver) 
| Internal merge handled by `RestSessionOptionAllowlistMerge` | Internal class 
— only relevant if reflectively referenced. The `juneau-commons` 
`StringTokenSet` survives as a general-purpose token-set utility. |
+| `RestAllowedSerializerOptions` / `RestAllowedParserOptions` | Removed — keys 
live on `@Rest` / `@RestOp(allowedSerializerOptions / allowedParserOptions)` 
annotation attributes | Hard removal. |
+| `RestAllowedSessionOptionsMerge` / `NoInheritUtils` | Removed | Internal 
merge helpers — no replacement needed. |
+| `RestContext.Builder.appendAllowedSerializerOptions(...)` / 
`appendAllowedParserOptions(...)` / `allowedSerializerOptions(...)` / 
`allowedParserOptions(...)` setters | Express via 
`@Rest(allowedSerializerOptions=..., allowedParserOptions=...)` annotation 
attributes | The programmatic builder setters are gone with the builder itself. 
|
+| `RestOpContext.Builder.appendAllowed*()` / `allowedSerializerOptions(...)` / 
`allowedParserOptions(...)` setters | Express via 
`@RestOp(allowedSerializerOptions=..., allowedParserOptions=...)` annotation 
attributes | Same. |
+| `RestSessionOptionWire` static literals | `RestSharedConstants` in 
`juneau-rest-common` — `HEADER_JuneauSerializerOptions`, 
`HEADER_JuneauParserOptions`, `QUERY_juneauSerializerOptions`, 
`QUERY_juneauParserOptions` | Class rename. |
+| `RestSessionOptionsSettings` static literals | `RestServerConstants` in 
`juneau-rest-server` — `SETTING_sessionOptions_rejectWhenAllowlistEmpty`, 
`SETTING_sessionOptions_failOnInvalidAllowlistEntry`, etc. | Class rename. |
+
+## Legacy `cp` Bean-Store Classes Removed (TODO-26)
+
+The pre-existing legacy bean-store / bean-creator classes in 
`org.apache.juneau.cp` have been deleted. The replacement types live in 
`org.apache.juneau.commons.inject` (introduced in TODO-24 / TODO-15).
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `org.apache.juneau.cp.BasicBeanStore` | 
`org.apache.juneau.commons.inject.BasicBeanStore` (renamed from 
`BasicBeanStore2`) | Constructor: `BasicBeanStore.create().build()` → `new 
BasicBeanStore()`. Static `INSTANCE` field preserved. 
`BasicBeanStore.of(parent)` → `new BasicBeanStore(parent)`. |
+| `org.apache.juneau.cp.BeanCreator` | 
`org.apache.juneau.commons.inject.BeanInstantiator` | Hard rename. 
`BeanCreator.of(MyBean.class, store)` → `BeanInstantiator.of(MyBean.class, 
store)`. |
+| `creator.arg(Type.class, value)` | `instantiator.addBean(Type.class, value)` 
| Method rename. |
+| `creator.type(Impl.class)` / `creator.impl(instance)` / `creator.run()` | 
`instantiator.type(Impl.class)` / `instantiator.impl(instance)` / 
`instantiator.run()` | Method names unchanged. |
+| `creator.orElse(default)` | `instantiator.asOptional().orElse(default)` | 
Two-step replacement. |
+| `org.apache.juneau.cp.BeanBuilder` (base class for domain fluent builders) | 
`org.apache.juneau.commons.inject.BeanInstantiator` plus native builder fields 
on the domain types | Hard removal. Domain builders now use native fields 
instead of extending `BeanBuilder`. |
+| `org.apache.juneau.cp.BeanCreateMethodFinder` | 
`BeanStore.createBeanFromMethod(type, obj, predicate)` | Hard rename — 
method-on-store rather than free-standing finder class. |
+| `org.apache.juneau.cp.ContextBeanCreator` | Retained at 
`org.apache.juneau.cp.ContextBeanCreator` | **Not** removed. Solves a different 
problem (persistent `Context.Builder` holder for repeated annotation 
application). |
+| `org.apache.juneau.commons.inject.CreatableBeanStore` | Removed | Unused 
internal interface. `WritableBeanStore` is the canonical write-capable 
interface. |
+
+## Bean-Store Convenience Renames
+
+The `Xxx2`-suffix transitional types have been promoted to the canonical name:
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `org.apache.juneau.commons.inject.BasicBeanStore2` | 
`org.apache.juneau.commons.inject.BasicBeanStore` | Hard rename. The legacy 
`org.apache.juneau.cp.BasicBeanStore` is deleted — see "Legacy `cp` Bean-Store 
Classes Removed (TODO-26)" above. |
+| `org.apache.juneau.rest.springboot.SpringBeanStore2` | 
`org.apache.juneau.rest.springboot.SpringBeanStore` | Hard rename. The old name 
is removed. |
+| Several builder fields/constructors typed as `WritableBeanStore` on 
`EncoderSet.Builder`, `SerializerSet.Builder`, `ParserSet.Builder`, 
`ResponseProcessorList.Builder`, `RestOpArgList.Builder`, 
`RestMatcherList.Builder`, `RestGuardList.Builder`, 
`RestConverterList.Builder`, `RestOperations.Builder`, `RestChildren.Builder` | 
Narrowed to `BeanStore` (read-only) | Source-compatible — `WritableBeanStore 
extends BeanStore`, so existing call sites pass through unchanged. The narrower 
type cl [...]
+
+## Swagger v2: `ParamInfo` → `ParameterInfo`
+
+The legacy `ParamInfo` type in the Swagger v2 bean module 
(`org.apache.juneau.bean.swagger.ParamInfo`) has been fully replaced with 
`ParameterInfo`. All references in `juneau-bean-swagger` have been migrated.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `org.apache.juneau.bean.swagger.ParamInfo` | 
`org.apache.juneau.bean.swagger.ParameterInfo` | Hard rename. Update imports 
and references. |
+
+## Multi-Key Cache and `ConcurrentHashMapXKey` Refactor (9.2.0)
+
+`juneau-commons` introduced a dedicated `Cache<...>` family that separates 
caching concerns from the base concurrent maps. The base maps were renamed for 
naming-convention consistency, and their constructors no longer accept caching 
parameters.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `Concurrent2KeyHashMap` | `ConcurrentHashMap2Key` | Hard rename for 
naming-convention consistency. |
+| `Concurrent3KeyHashMap` | `ConcurrentHashMap3Key` | Same. |
+| `Concurrent4KeyHashMap` | `ConcurrentHashMap4Key` | Same. |
+| `Concurrent5KeyHashMap` | `ConcurrentHashMap5Key` | Same. |
+| `new ConcurrentXKeyHashMap(boolean disabled, Function supplier, ...)` 
constructors carrying caching state | Pure multi-key concurrent maps (`extends 
ConcurrentHashMap<TupleX<...>, V>`); for caching use the new sibling 
`Cache<K,V>` / `Cache2<K1,K2,V>` / `Cache3` / `Cache4` / `Cache5` 
builder-pattern API (`Cache2.of(K1.class, K2.class, 
V.class).maxSize(...).supplier(...).build()`) | Code using only the multi-key 
map needs a class-name update. Code using `disabled` / `supplier` parameters 
[...]
+| Null key in `ConcurrentXKeyHashMap.get(...)` / `put(...)` previously 
bypassed the cache silently | Throws `IllegalArgumentException` | **Hard 
behavioral break.** |
+
+## Module Rename: `juneau-all` → `juneau-shaded-all` (9.2.0)
+
+The previous `juneau-all` Maven module has been **removed** in favor of 
`juneau-shaded-all`, part of the new `juneau-shaded` family of shaded (uber) 
JAR artifacts. No code changes are required — all imports and APIs are 
unchanged.
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `<artifactId>juneau-all</artifactId>` | 
`<artifactId>juneau-shaded-all</artifactId>` | Hard rename. Consider the more 
specific shaded artifacts (`juneau-shaded-core`, `juneau-shaded-rest-client`, 
`juneau-shaded-rest-server`, `juneau-shaded-rest-server-springboot`) if you 
don't need the full framework. |
+
+## Miscellaneous Utility Removals
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `Tuple2Function` / `Tuple3Function` / `Tuple4Function` / `Tuple5Function` | 
`Function2` / `Function3` / `Function4` / `Function5` | Hard rename — API is 
equivalent (same arity, same `apply(...)` signature). |
+| `Console.format(String, Object...)` | `Utils.f(String format, Object... 
args)` | Hard removal — duplicated functionality. `Console.err(...)` / 
`Console.out(...)` are unchanged. |
+| `BasicRuntimeException` (concrete subclass used to wrap arbitrary checked 
exceptions) | `ThrowableUtils.runtimeException(...)` (with optional `Throwable 
cause` overload) | Retired in favor of the `ThrowableUtils` factory. Use 
`runtimeException(msg, args)` or `runtimeException(cause, msg, args)`. New 
helpers in `ThrowableUtils`: `unsupportedOp(String, Object...)`, 
`ioException(String, Object...)`, plus cause-accepting overloads of 
`illegalArg(...)` / `runtimeException(...)`. |
+| `ArrayUtils` static methods | `CollectionUtils` static methods (same 
signatures: `last()`, `append()`, `combine()`, `indexOf()`, `toList()`, 
`contains()`, etc.) | `ArrayUtils` is now `@Deprecated` and delegates to 
`CollectionUtils`. Migrate at your convenience; both will continue to work 
during the deprecation period. |
+| `ResettableSupplier` | `OptionalSupplier` | Class rename. |
+
+## BCT: `AssertionArgs` Removed
+
+| Old | New | Notes |
+|-----|-----|-------|
+| `AssertionArgs.withConverter(customConverter).assertBean(expected, actual, 
"path")` | `setConverter(() -> customConverter); assertBean(expected, actual, 
"path"); resetConverter();` | Hard removal of the `AssertionArgs` builder. 
Assertion methods now take an optional leading `Supplier<String>` parameter for 
custom messages: `assertBean(() -> "custom msg", expected, actual, 
"propertyPath")`. The default thread-local converter supplier is 
`BasicBeanConverter.DEFAULT`. |
+
+## OpenAPI 3.1 Emission + `@Rest(apiFormat=…)` Knob (TODO-63)
+
+OpenAPI 3.1 emission is new in 9.5; existing Swagger v2 emission is unchanged. 
The new `@Rest(apiFormat=…)` attribute selects which spec format the canonical 
`/api/*` endpoint serves and whether the new `/openapi/*` sibling is mounted. 
The default is `"swagger"` (back-compat).
+
+| Old | New | Notes |
+|-----|-----|-------|
+| Resource exposed only Swagger v2 at `/api/*`. No way to opt into OpenAPI 3.1 
emission. | `@Rest(apiFormat="openapi")` opts the resource into OpenAPI 3.1 + 
Redoc on `/openapi/*` (and 404s `/api/*`). `@Rest(apiFormat="both")` mounts 
Swagger v2 + Swagger UI on `/api/*` AND OpenAPI 3.1 + Redoc on `/openapi/*`. 
Default `"swagger"` is unchanged behavior. | System-property override: 
`juneau.rest.apiFormat`. Resolution precedence: annotation → system property → 
default. Recognized values are ` [...]
+| `BasicRestOperations.getSwagger(RestRequest)` and (new) 
`getOpenApi(RestRequest)` were going to be abstract methods that every 
implementer had to override. | Both are now `default` methods on 
`BasicRestOperations` that consult `RestContext.getApiFormat()` and either 
delegate to `RestRequest.getSwagger()` / `getOpenApi()` or throw `NotFound` 
based on the resolved format. | Subclasses (`BasicRestObject`, 
`BasicRestServlet`, `BasicSpringRestServlet`) no longer override these methods. 
User [...]
+| `RestRequest.getSwagger()` was the only way to ask the server for its 
self-described API. | `RestRequest.getOpenApi()` provides the OpenAPI 3.1 
sibling. `RestContext.getOpenApi(Locale)` and 
`RestContext.getOpenApiProvider()` mirror the Swagger getters. | The OpenAPI 
3.1 document is generated by transforming the Swagger 2.0 emission to OpenAPI 
3.1 JSON, so every Swagger-aware annotation (`@Schema`, `@Content`, 
`@StatusCode`, etc.) round-trips with no source changes. |
+
+Reference: see the per-module section in the 9.5 release notes (`### 
juneau-rest-server` → `OpenAPI 3.1 Emission + apiFormat Knob (TODO-63)`).
+
+<!-- Migration guide complete for the 9.1 → 9.5 jump. Add new entries here as 
further 9.5.x
+or 9.6 breaking changes land. -->
diff --git a/sidebars.ts b/sidebars.ts
index a871cd04b5..8c1be7b83b 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -1327,6 +1327,11 @@ const sidebars: SidebarsConfig = {
                                                        id: 
'topics/10.07.HandlingFormPosts',
                                                        label: '10.7. Handling 
Form Posts',
                                                },
+                                               {
+                                                       type: 'doc',
+                                                       id: 
'topics/10.08.RestServerSse',
+                                                       label: '10.8. 
Server-Sent Events',
+                                               },
                                                {
                                                        type: 'doc',
                                                        id: 
'topics/10.08.Guards',
@@ -1471,6 +1476,11 @@ const sidebars: SidebarsConfig = {
                                                        id: 
'topics/10.20.HttpStatusCodes',
                                                        label: '10.20. HTTP 
Status Codes',
                                                },
+                                               {
+                                                       type: 'doc',
+                                                       id: 
'topics/10.20a.RestServerProblemDetails',
+                                                       label: '10.20a. RFC 
7807 / 9457 Problem Details',
+                                               },
                                                {
                                                        type: 'doc',
                                                        id: 
'topics/10.21.BuiltInParameters',

Reply via email to