This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git

commit a972ff62067dfe41ad557124144c84a850d81c00
Author: James Bognar <[email protected]>
AuthorDate: Tue May 12 13:24:27 2026 -0400

    docs: update TODO-5 plan with Step 8a status and Step 8b breakdown
    
    Step 8a is committed as 3a74fcd50a. Update the plan to:
    - Mark Step 8a [x] with the commit hash.
    - Split Step 8 into 8a/8b/8c; 8b is the physical git mv, 8c is optional 
cleanup.
    - Move the recommended-next-checkpoint pointer to Step 8b.
    - Spell out the remaining SPI seams that Step 8b has to extract 
(BeanTypeInfo
      for ClassMeta, BeanRegistryLookup for BeanRegistry, optional 
BeanPropertySwap
      for ObjectSwap) and the design options for each.
    - Document why moving the 8 types one-at-a-time is not viable (tight 
cluster of
      cross-references) — they have to move as a unit.
    - List the remaining marshalling-side references that survived Step 8a:
      ClassMeta, BeanRegistry, ObjectSwap, MarshalledFilter, MarshalledProp,
      Json5Serializer, JsonMap/JsonList — with notes on each one's migration 
shape.
    
    No code changes; documentation/plan update only.
    
    Co-authored-by: Cursor <[email protected]>
---
 todo/TODO-5-bean-runtime-types-to-commons.md | 43 ++++++++++++++++++++++------
 1 file changed, 35 insertions(+), 8 deletions(-)

diff --git a/todo/TODO-5-bean-runtime-types-to-commons.md 
b/todo/TODO-5-bean-runtime-types-to-commons.md
index 4b8b3eb699..56d740ecbd 100644
--- a/todo/TODO-5-bean-runtime-types-to-commons.md
+++ b/todo/TODO-5-bean-runtime-types-to-commons.md
@@ -4,7 +4,25 @@ This is the remaining work from **Phase 5 of the bean-layer 
split**. Phase 5a (t
 
 ---
 
-## Status (as of Phase 5g checkpoint)
+## Status (as of Phase 5h checkpoint)
+
+**Step 8a complete (SPI-seam extraction).** Commit `3a74fcd50a`. The minimum 
SPI surface that the 8 target types need from the marshalling layer is now in 
place:
+
+- New SPI: `org.apache.juneau.commons.bean.BeanSession`. Captures the 
session-aware operations (`convertToType`, `convertToMemberType`, `toBeanMap`) 
that `BeanMap`/`BeanPropertyMeta` need from `MarshallingSession`. The 
`targetType` parameters are typed as `Object` so `commons.bean` does not need 
to import `ClassMeta`. The `toBeanMap` method is generic (`<T> Object 
toBeanMap(T bean)`) so `MarshallingSession`'s existing covariant `<T> 
BeanMap<T> toBeanMap(T)` satisfies it without a bridge method.
+- `MarshallingSession` now implements `BeanSession`. Two new bridge methods 
(`convertToType(Object,Object)` / `convertToMemberType(Object,Object,Object)`) 
dispatch `Object`-typed `targetType` arguments into the existing `ClassMeta` / 
`Class` typed overloads. `IllegalArgumentException` is thrown for unsupported 
`targetType` kinds.
+- `BeanConfigContext` now carries four additional bean-modeling settings that 
previously only lived on `MarshallingContext`: `beanMapPutReturnsOldValue`, 
`ignoreInvocationExceptionsOnGetters`, `ignoreInvocationExceptionsOnSetters`, 
`ignoreUnknownNullBeanProperties`. Defaults match `MarshallingContext`'s 
historical defaults. Builder, copy() and DEFAULT are wired through.
+- `MarshallingContext.buildBeanConfigContext()` populates the four new 
settings from its own resolved values, so behavior is unchanged.
+- `BeanPropertyMeta` reads those four settings through `config.isX()` instead 
of `bc.isX()`. The corresponding `nn(bc) && bc.isX()` guards from Step 6 are 
simplified to unconditional `config.isX()` reads.
+- `BeanConfigContext_Test` extended to exercise the new defaults, builder 
setters, and copy() preservation.
+
+What the 8 target types still pull from `juneau-marshall` (left for Step 8b's 
SPI work + physical move):
+1. `ClassMeta` — used as the type metadata for property `rawTypeMeta` / 
`typeMeta`, the `BeanMeta.classMeta` field, the `applyChildPropertiesFilter` 
parameter, and `BeanMap.getBean()`'s Optional-init / constructor-args paths. 
Needs either: a `BeanTypeInfo` SPI typed in `commons.bean`, or `Object`-typed 
field with marshalling-side narrowing, or migration of consumption to 
marshalling-side helpers.
+2. `BeanRegistry` — used by `BeanMeta`'s side-map 
(`Map<BeanPropertyMeta,BeanRegistry> propertyBeanRegistries`) and per-property 
registry construction inside `BeanMeta` from `Builder.dictionaryClasses`. Needs 
a `BeanRegistryLookup` SPI (or migrate the side-map to the marshalling-side).
+3. `ObjectSwap` — referenced by `BeanPropertyMeta.swap` field and 
`BeanMap.getBean()`'s Optional handling. The actual swap/unswap calls already 
route through `readTransform`/`writeTransform` (Step 3); the field itself is 
just metadata for the defensive double-unswap check. Needs either: a 
`BeanPropertySwap` SPI, or move the field to a side-map on `BeanMeta`.
+4. `MarshalledFilter` — used by `BeanMeta` (composed via 
`getMarshalledFilter()`), by `BeanMetaFiltered` (passes through to `BeanMeta`'s 
old constructor), and by `MarshalledProp` annotation reads inside 
`BeanPropertyMeta.Builder.validate()`. Likely stay-behind on the marshalling 
side; `BeanMetaFiltered` migrates with `BeanMeta` and the `MarshalledFilter` 
parameter on the old constructor stays `Object`-typed (or moves to a 
marshalling-side wrapper).
+5. `MarshalledProp` annotation — read in `BeanPropertyMeta.Builder.validate()` 
to detect filtered properties. Marshalling-side annotation; needs either a 
`commons.bean` mirror or be lifted out of `validate()` into a marshalling-side 
post-processor.
+6. `Json5Serializer` — used by `BeanProxyInvocationHandler.toString()` only. 
Trivial to replace with a `BeanSession`-style hook or just inline minimal 
JSON5-ish formatting.
+7. `JsonMap` / `JsonList` — used inside `BeanPropertyMeta`/`BeanMap` for 
collection/map building during set operations. Likely route through 
`BeanSession.toBeanMap`/converter callbacks, or accept `Map`/`List` instead.
 
 **Step 7 complete (no-op + hardening).** Per-format extension survey 
confirmed: `ExtendedBeanMeta` (composes `BeanMeta<?>`) lives in 
`juneau-marshall`; `XmlBeanMeta` and `RdfBeanMeta` extend `ExtendedBeanMeta`; 
no `HtmlBeanMeta` exists. All three are marshalling-side types (built only from 
`XmlSerializer`/`XmlParser`/`RdfSerializer`/`RdfParser`, always over a 
marshalling-built `BeanMeta` where `classMeta` is non-null). They stay in 
`juneau-marshall` unchanged.
 
@@ -71,16 +89,25 @@ Known limitations of the commons-side path (acceptable for 
Step 6, scoped for la
 - [x] **Step 5** — Removed `BeanRegistry` field from `BeanPropertyMeta`. 
Picked **Option B** — side-map keyed by `BeanPropertyMeta` lives on `BeanMeta` 
(`Map<BeanPropertyMeta,BeanRegistry> propertyBeanRegistries`). Per-property 
registries are constructed by `BeanMeta` after `v.build()` from the builder's 
package-private `dictionaryClasses` field (populated during 
`Builder.validate(...)`). `Builder.beanRegistry(...)` public setter, the 
`beanRegistry` builder field, and the `parentBeanRegi [...]
 - [x] **Step 6** — `BeanMeta.of(Class<T>, BeanConfigContext)` factory + 
`protected BeanMeta(Class<T>, BeanConfigContext)` constructor wired up. 
`BeanMeta` now carries a non-null `BeanConfigContext config` facade for all 
settings reads; the `marshallingContext` and `classMeta` fields are 
documented-nullable and stay null on the commons-side path. 
`BeanPropertyMeta.Builder.bc` and `BeanPropertyMeta.bc` similarly nullable; new 
mirrored `config` field on both. `Builder.validate(...)` accepts [...]
 - [x] **Step 7** — Per-format extension survey + pre-Step-8 hardening. (a) 
Survey result: `ExtendedBeanMeta` (composes `BeanMeta<?>`), `XmlBeanMeta` 
(extends `ExtendedBeanMeta`), `RdfBeanMeta` (extends `ExtendedBeanMeta`); no 
`HtmlBeanMeta` exists. All marshalling-side, all stay in `juneau-marshall`. (b) 
Hardened `BeanPropertyMeta.add(BeanMap,String,Object)` / 
`add(BeanMap,String,String,Object)` / `setArray` / `applyChildPropertiesFilter` 
(all throw `UnsupportedOperationException` with a [...]
-- [ ] **Step 8** — `git mv` the eight runtime types into 
`juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/`. 
Verify `juneau-commons` still compiles standalone (`cd 
juneau-core/juneau-commons && mvn clean compile`).
+- [x] **Step 8a** — SPI seams in `commons.bean` (commit `3a74fcd50a`). 
`BeanSession` interface created, `MarshallingSession` implements it, four 
marshalling-only-historically settings migrated to `BeanConfigContext` so 
`BeanPropertyMeta`/`BeanMap` can stop reading them through 
`MarshallingContext`. See "Status (as of Phase 5h checkpoint)" above for full 
detail.
+- [ ] **Step 8b** — Round out the remaining SPI seams (`BeanTypeInfo` for 
`ClassMeta`, `BeanRegistryLookup` for `BeanRegistry`, optional 
`BeanPropertySwap` for `ObjectSwap`), retype the 8 types' fields to use the 
seams (or `Object`) on the bean-modeling side, then `git mv` all 8 files into 
`juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/`. 
Verify `juneau-commons` still compiles standalone (`cd 
juneau-core/juneau-commons && mvn clean compile`).
+- [ ] **Step 8c** — (optional) Cleanup pass for anything that comes up during 
8b: deprecated bridges, stale imports, package-info docs, etc.
 - [ ] **Step 9** — Reference sweep: 80–120 unique files (mostly inside 
`juneau-marshall`). Update imports, Javadoc `{@link …}` references, 
package-info docs.
 - [ ] **Step 10** — Update `juneau-docs` release notes / migration guide 
(`docs/pages/release-notes/9.5.0.md`, `## Package Moves` section) with the 
bean-runtime relocations.
 
-The "incomplete-but-documented over broken-build" rule from Phase 5a still 
applies. When picking up the next slice of this work, **Step 8 is the 
recommended next checkpoint** — the physical `git mv` of the eight runtime 
types into 
`juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/bean/`. 
Pre-flight is now clean: per-format `BeanMeta` extensions 
(`ExtendedBeanMeta`/`XmlBeanMeta`/`RdfBeanMeta`) are confirmed marshalling-side 
and stay put (Step 7); the previously unguarded [...]
-
-**Remaining risks/surprises for Step 8:**
-- The eight files reference 
`MarshallingContext`/`MarshallingSession`/`ClassMeta`/`BeanRegistry`/`ObjectSwap`
 in many places that survived Step 6/7 (the `installSwapAwareTransforms` 
install path, the `BeanRegistry` side-map on `BeanMeta`, the 
`MarshallingSession` field on `BeanMap`, the `ClassMeta classMeta` field on 
`BeanMeta`, etc.). Physically moving the files into `commons.bean` will require 
either: (a) leaving these fields in place but moving the types, accepting that 
`juneau-common [...]
-- `BeanPropertyMeta.applyChildPropertiesFilter` directly takes a `ClassMeta` 
parameter — the signature itself encodes a marshalling-side type. If it stays 
on `BeanPropertyMeta` after the move, `commons.bean` will need a forward 
reference to `ClassMeta` (which is in `juneau-marshall`). Likely needs to 
migrate to a side helper in the marshalling layer.
-- `BeanMap.getBean(boolean create)`'s constructor-args path uses 
`session.convertToType(rawVal, cm)` — this entire block is `@BeanCtor` 
parser-side behavior. Should migrate to a marshalling-side helper after the 
move (Step 9 or later).
+The "incomplete-but-documented over broken-build" rule from Phase 5a still 
applies. **The recommended next checkpoint is Step 8b** — extracting the 
remaining SPI seams that the 8 types need from the marshalling layer, then 
doing the physical `git mv`. Step 8a is the prerequisite (`BeanSession` is in 
place and `BeanConfigContext` already covers the bean-modeling boolean 
settings); Step 8b can build on those.
+
+**Remaining risks/surprises for Step 8b:**
+- The 8 target types are tightly clustered: `BeanMap` ↔ `BeanPropertyMeta` ↔ 
`BeanMeta` ↔ `BeanMapEntry` ↔ `BeanPropertyValue` ↔ `BeanPropertyConsumer` ↔ 
`BeanMetaFiltered` ↔ `BeanProxyInvocationHandler`. They reference each other in 
field declarations and method signatures, so they have to move **as a unit**. 
Moving just the leaf types (e.g. `BeanPropertyConsumer`, `BeanMapEntry`) would 
create circular cross-module references because they reference 
`BeanPropertyMeta`/`BeanMap` which sta [...]
+- `ClassMeta` is the biggest seam to design. Options:
+  - **(a)** Introduce `BeanTypeInfo` interface in `commons.bean` (minimum 
surface: `inner()`, `isPrimitive()`, `isOptional()`, `getOptionalDefault()`, 
`isAssignableTo`, `isCollection()`, `isMap()`, `isString()`, `isObject()`, 
`getElementType()`, `getValueType()`, `getKeyType()`, `getInfo()`); have 
`ClassMeta` implement it. Pro: minimal API churn on the 8 types. Con: large 
interface to design carefully.
+  - **(b)** Retype the fields as `Object` on the bean-modeling side; cast to 
`ClassMeta` in narrow marshalling-side helpers. Pro: zero new SPI surface. Con: 
stringly-typed; harder to read.
+  - **(c)** Move the consumption sites (e.g. `BeanMap.getBean()`'s 
Optional-init, `applyChildPropertiesFilter`) into marshalling-side helpers; 
leave `BeanMap`/`BeanPropertyMeta` with no `ClassMeta` references at all. Pro: 
cleanest commons.bean. Con: refactors marshalling-side code along with the move.
+- `BeanRegistry` side-map (`Map<BeanPropertyMeta,BeanRegistry> 
propertyBeanRegistries`) on `BeanMeta` is currently typed against the concrete 
marshalling-side `BeanRegistry`. Either: keep the map but type its value as 
`Object` (commons-side narrowing-as-needed), or introduce a 
`BeanRegistryLookup` interface (`String getTypeName(Class<?>)` + `Class<?> 
getClassMeta(String)` is probably all that's needed).
+- `ObjectSwap` field on `BeanPropertyMeta` is only metadata after Step 3 — the 
actual swap/unswap calls go through `readTransform`/`writeTransform`. Probably 
the cleanest move is to retype `swap` as `Object` and let the few callers 
(`setPropertyValue`'s defensive double-unswap *check*, `BeanMapEntry`'s 
Javadoc) live with that.
+- `BeanMetaFiltered` extends `BeanMeta` and calls 
`super(innerMeta.getClassMeta(), innerMeta.getMarshalledFilter(), pNames, 
null)`. The old constructor signature (`BeanMeta(ClassMeta<T>, 
MarshalledFilter, String[], ClassInfo)`) carries `ClassMeta` and 
`MarshalledFilter` as parameters. After the move, that constructor either: 
stays on `BeanMeta` typed against `Object`s, or moves to a marshalling-side 
factory helper. The new `BeanMeta.of(Class, BeanConfigContext)` factory chain 
from Step 6 [...]
+- `MarshalledProp` annotation read inside 
`BeanPropertyMeta.Builder.validate()` is the only direct marshalling-annotation 
reference in the 8 types' source. Either: lift the read out into a 
marshalling-side post-processor that mutates the `Builder` before `build()`, or 
move `MarshalledProp` to `commons.bean` (it's already paired with 
`MarshalledFilter` which is squarely marshalling-side, so the lift-out is 
cleaner).
+- `BeanProxyInvocationHandler.toString()` uses 
`Json5Serializer.DEFAULT.toString(...)`. Trivially replaceable with 
`Objects.toString(...)` or routed through a `BeanSession`-style formatter.
 
 ---
 

Reply via email to