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 c9f7601485d1dfd1c424da4de3eabcbc16edd6b6 Author: James Bognar <[email protected]> AuthorDate: Tue May 12 11:37:54 2026 -0400 refactor: replace ClassMeta with ClassInfo in BeanMeta/BeanPropertyMeta (TODO-5 Step 2) Co-authored-by: Cursor <[email protected]> --- .../src/main/java/org/apache/juneau/BeanMeta.java | 48 +++++++++++----------- .../java/org/apache/juneau/BeanPropertyMeta.java | 15 +++++++ todo/TODO-5-bean-runtime-types-to-commons.md | 4 +- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java index fc12d17631..f464590b26 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanMeta.java @@ -361,6 +361,7 @@ public class BeanMeta<T> { private final Supplier<BeanRegistry> beanRegistry; // The bean registry for this bean. private final Supplier<List<ClassInfo>> classHierarchy; // List of all classes traversed in the class hierarchy. private final ClassMeta<T> classMeta; // The target class type that this meta object describes. + private final ClassInfo classInfo; // Pure-reflection view of the bean class (Step 2 of TODO-5 — decouples bean modeling from ClassMeta). private final Supplier<String> dictionaryName; // The @Marshalled(typeName) annotation defined on this bean class. private final BeanPropertyMeta dynaProperty; // "extras" property. @SuppressWarnings("rawtypes") @@ -405,6 +406,7 @@ public class BeanMeta<T> { }) protected BeanMeta(ClassMeta<T> cm, MarshalledFilter bf, String[] pNames, ClassInfo implClass) { classMeta = cm; + classInfo = cm; marshallingContext = cm.getMarshallingContext(); beanFilter = bf; implClassConstructor = opt(implClass).map(x -> x.getPublicConstructor(x2 -> x2.hasNumParameters(0)).orElse(null)).orElse(null); @@ -416,8 +418,8 @@ public class BeanMeta<T> { // Local variables for initialization var ap = marshallingContext.getAnnotationProvider(); - var c = cm.inner(); - var ci = cm; + var c = classInfo.inner(); + var ci = classInfo; String notABeanReasonTemp = null; var propertiesValue = Value.<Map<String,BeanPropertyMeta>>empty(); var hiddenPropertiesMap = CollectionUtils.<String,BeanPropertyMeta>map(); @@ -446,7 +448,7 @@ public class BeanMeta<T> { fixedBeanProps.forEach(x -> normalProps.put(x, BeanPropertyMeta.builder(this, x))); if (marshallingContext.isUseJavaBeanIntrospector()) { - var c2 = bfo.map(x -> x.getInterfaceClass()).filter(Objects::nonNull).orElse(classMeta); + var c2 = bfo.map(x -> x.getInterfaceClass()).filter(Objects::nonNull).orElse(classInfo); BeanInfo bi = null; if (! c2.isInterface()) bi = Introspector.getBeanInfo(c2.inner(), stopClass.inner()); @@ -605,9 +607,9 @@ public class BeanMeta<T> { setterProps = u(setterPropsMap); dynaProperty = dynaPropertyValue.get(); unsortedProperties = unsortedPropertiesTemp; - typeProperty = BeanPropertyMeta.builder(this, typePropertyName).canRead().canWrite().rawMetaType(marshallingContext.string()).beanRegistry(beanRegistry.get()).build(); + typeProperty = BeanPropertyMeta.builder(this, typePropertyName).canRead().canWrite().rawMetaType(String.class).beanRegistry(beanRegistry.get()).build(); dictionaryName = memoize(this::findDictionaryName); - beanProxyInvocationHandler = memoize(()->marshallingContext.isUseInterfaceProxies() && c.isInterface() ? new BeanProxyInvocationHandler<>(this) : null); + beanProxyInvocationHandler = memoize(() -> marshallingContext.isUseInterfaceProxies() && classInfo.isInterface() ? new BeanProxyInvocationHandler<>(this) : null); var factoryClassTemp = btList.stream().map(x -> x.inner().factory()).filter(x -> x != org.apache.juneau.commons.function.BeanFactory.Void.class).findFirst().orElse(null); factoryClass = factoryClassTemp; } @@ -638,7 +640,7 @@ public class BeanMeta<T> { @Override /* Overridden from Object */ public boolean equals(Object o) { - return (o instanceof BeanMeta<?> o2) && eq(this, o2, (x, y) -> eq(x.classMeta, y.classMeta)); + return (o instanceof BeanMeta<?> o2) && eq(this, o2, (x, y) -> eq(x.classInfo, y.classInfo)); } /** @@ -760,7 +762,7 @@ public class BeanMeta<T> { @Override /* Overridden from Object */ public int hashCode() { - return classMeta.hashCode(); + return classInfo.hashCode(); } /** @@ -796,7 +798,7 @@ public class BeanMeta<T> { protected FluentMap<String,Object> properties() { // @formatter:off return filteredBeanPropertyMap() - .a(PROP_class, classMeta.getName()) + .a(PROP_class, classInfo.getName()) .a(PROP_properties, properties); // @formatter:on } @@ -953,16 +955,16 @@ public class BeanMeta<T> { throw new ExecutableException(e); } } - if (classMeta.isMemberClass() && classMeta.isNotStatic()) { + if (classInfo.isMemberClass() && classInfo.isNotStatic()) { if (hasConstructor()) return getConstructor().<T>newInstance(outer); } else { if (hasConstructor()) return getConstructor().<T>newInstance(); - var h = classMeta.getProxyInvocationHandler(); + var h = beanProxyInvocationHandler.get(); if (nn(h)) { - var cl = classMeta.getClassLoader(); - return (T)Proxy.newProxyInstance(cl, a(classMeta.inner(), java.io.Serializable.class), h); + var inner = classInfo.inner(); + return (T)Proxy.newProxyInstance(inner.getClassLoader(), a(inner, java.io.Serializable.class), h); } } return null; @@ -1024,7 +1026,7 @@ public class BeanMeta<T> { private BeanConstructor findBeanConstructor() { var ap = marshallingContext.getAnnotationProvider(); var vis = marshallingContext.getBeanConstructorVisibility(); - var ci = classMeta; + var ci = classInfo; var l = ci.getPublicConstructors().stream().filter(x -> ap.has(BeanCtor.class, x)).toList(); if (l.isEmpty()) @@ -1057,8 +1059,8 @@ public class BeanMeta<T> { if (implClassConstructor != null) return new BeanConstructor(opt(implClassConstructor.accessible()), liste()); - var ba = ap.find(Marshalled.class, classMeta); - var btList = ap.find(org.apache.juneau.commons.bean.BeanType.class, classMeta); + var ba = ap.find(Marshalled.class, classInfo); + var btList = ap.find(org.apache.juneau.commons.bean.BeanType.class, classInfo); var con = ci.getNoArgConstructor((! ba.isEmpty() || ! btList.isEmpty()) ? Visibility.PRIVATE : vis).orElse(null); if (con != null) return new BeanConstructor(opt(con.accessible()), liste()); @@ -1109,9 +1111,9 @@ public class BeanMeta<T> { var v = marshallingContext.getBeanFieldVisibility(); var noIgnoreTransients = ! marshallingContext.isIgnoreTransientFields(); var ap = marshallingContext.getAnnotationProvider(); - var isRecord = classMeta.isRecord(); + var isRecord = classInfo.isRecord(); var recordComponentNames = isRecord - ? classMeta.getRecordComponents().stream().map(java.lang.reflect.RecordComponent::getName).collect(java.util.stream.Collectors.toSet()) + ? classInfo.getRecordComponents().stream().map(java.lang.reflect.RecordComponent::getName).collect(java.util.stream.Collectors.toSet()) : Set.<String>of(); // @formatter:off return classHierarchy.get().stream() @@ -1176,7 +1178,7 @@ public class BeanMeta<T> { private List<BeanMethod> findBeanMethods() { var l = new LinkedList<BeanMethod>(); var ap = marshallingContext.getAnnotationProvider(); - var ci = classMeta; + var ci = classInfo; var v = marshallingContext.getBeanMethodVisibility(); var pn = opt(beanFilter).map(x -> x.getPropertyNamer()).orElse(marshallingContext.getPropertyNamer()); var suppressedFromBeanIgnoredFields = findSuppressedPropertyNamesFromIgnoredFields(pn); @@ -1313,8 +1315,8 @@ public class BeanMeta<T> { var beanDictionaryClasses = opt(beanFilter).map(x -> new ArrayList<>(x.getBeanDictionary())).orElse(new ArrayList<>()); // Bean dictionary from @Marshalled(typeName) annotation. - var ba = marshallingContext.getAnnotationProvider().find(Marshalled.class, classMeta); - ba.stream().map(x -> x.inner().typeName()).filter(Utils::ne).findFirst().ifPresent(x -> beanDictionaryClasses.add(classMeta)); + var ba = marshallingContext.getAnnotationProvider().find(Marshalled.class, classInfo); + ba.stream().map(x -> x.inner().typeName()).filter(Utils::ne).findFirst().ifPresent(x -> beanDictionaryClasses.add(classInfo)); return new BeanRegistry(marshallingContext, null, beanDictionaryClasses); } @@ -1349,7 +1351,7 @@ public class BeanMeta<T> { var result = new LinkedList<ClassInfo>(); // If @Marshalled.interfaceClass is specified on the parent class, then we want // to use the properties defined on that class, not the subclass. - var c2 = (nn(beanFilter) && nn(beanFilter.getInterfaceClass()) ? beanFilter.getInterfaceClass() : classMeta); + var c2 = (nn(beanFilter) && nn(beanFilter.getInterfaceClass()) ? beanFilter.getInterfaceClass() : classInfo); findClassHierarchy(c2, stopClass, result::add); return u(result); } @@ -1420,7 +1422,7 @@ public class BeanMeta<T> { return s; } - var n = classMeta + var n = classInfo .getParentsAndInterfaces() .stream() .skip(1) @@ -1435,7 +1437,7 @@ public class BeanMeta<T> { if (n != null) return n; - return classMeta.getMarshallingContext().getAnnotationProvider().find(Marshalled.class, classMeta) + return marshallingContext.getAnnotationProvider().find(Marshalled.class, classInfo) .stream() .map(AnnotationInfo::inner) .filter(x -> ! x.typeName().isEmpty()) diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java index 72f1a580fc..1d749a4bc4 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanPropertyMeta.java @@ -166,6 +166,21 @@ public class BeanPropertyMeta implements Comparable<BeanPropertyMeta> { return this; } + /** + * Sets the raw metadata type for this bean property from a {@link Class}. + * + * <p> + * Convenience overload that resolves the supplied class to a {@link ClassMeta} via the property's + * {@link MarshallingContext}, allowing callers from the bean-modeling layer to seed the type without + * holding a {@link ClassMeta} reference. + * + * @param value The raw metadata type for this bean property. + * @return This object. + */ + public Builder rawMetaType(Class<?> value) { + return rawMetaType(bc.getClassMeta(assertArgNotNull(ARG_value, value))); + } + private static ObjectSwap marshalledPropSwap(AnnotationInfo<MarshalledProp> ai) { var p = ai.inner(); if (! p.format().isEmpty()) diff --git a/todo/TODO-5-bean-runtime-types-to-commons.md b/todo/TODO-5-bean-runtime-types-to-commons.md index b086acfdbe..1fc5b3b08a 100644 --- a/todo/TODO-5-bean-runtime-types-to-commons.md +++ b/todo/TODO-5-bean-runtime-types-to-commons.md @@ -9,7 +9,7 @@ This is the remaining work from **Phase 5 of the bean-layer split**. Phase 5a (t **Step 1 complete.** A `BeanConfigContext` POJO + builder now lives in `commons.bean`; `MarshallingContext.getBeanConfigContext()` returns a memoized snapshot view. The eight runtime types still live in `juneau-marshall` and still use `MarshallingContext` directly — Step 1 is purely additive infrastructure that future steps can lean on. - [x] **Step 1** — `BeanConfigContext` POJO + builder in `commons.bean`. Carries: visibility settings, all `beans*Require*` toggles, `findFluentSetters`, `unsortedProperties`, `useInterfaceProxies`, `useJavaBeanIntrospector`, `ignoreMissingSetters`, `ignoreTransientFields`, `ignoreUnknownBeanProperties`, `propertyNamer`, `beanTypePropertyName`, `notBeanPackageNames` / `notBeanPackagePrefixes` / `notBeanClasses`, `BeanStore`, `AnnotationProvider`, optional `Predicate<ClassInfo>` override [...] -- [ ] **Step 2** — Replace `ClassMeta` with `ClassInfo` in `BeanMeta` / `BeanPropertyMeta`. Most `cm.*` calls (`isAnonymousClass`, `isMemberClass`, `isAssignableTo`, `getModifiers`, `getRecordComponents`, `inner`, …) are pure reflection that already exists on `ClassInfo`. Two outliers — `cm.getProxyInvocationHandler()` (replace with a `BeanConfigContext` hook or move proxy creation into `BeanMeta`) and `cm.getMarshallingContext().string()` (use a plain `Class<String>`/`ClassInfo`). +- [x] **Step 2** — Replaced `ClassMeta` with `ClassInfo` for pure-reflection access inside `BeanMeta`. Added a `classInfo` field (a re-typed view of the same instance as `classMeta`, since `ClassMeta extends ClassInfoTyped extends ClassInfo`) and routed all reflection calls (`inner()`, `isMemberClass()`, `isNotStatic()`, `isAnonymousClass()`, `isRecord()`, `isInterface()`, `getRecordComponents()`, `getName()`, `getParentsAndInterfaces()`, `getPublicConstructors()`, `getDeclaredConstructo [...] - [ ] **Step 3** — Remove swap-aware `get/set` from `BeanPropertyMeta`. Add identity-default `BiFunction<Object,Object,Object>` callbacks (or a small `BeanPropertyTransform` SPI) so the marshalling layer installs swap-aware behavior at session construction. - [ ] **Step 4** — Remove `MarshallingSession` back-pointer from `BeanMap`. After Step 3, `BeanMap.get/put` are raw property reads/writes; `MarshallingSession.toBeanMap` wraps a `BeanMap` for serialization and applies swaps externally. - [ ] **Step 5** — Remove `BeanRegistry` field from `BeanPropertyMeta`. Lift dictionary metadata into a marshalling-side companion (`MarshalledPropertyMeta` or a side-map keyed by `BeanPropertyMeta`). @@ -19,7 +19,7 @@ This is the remaining work from **Phase 5 of the bean-layer split**. Phase 5a (t - [ ] **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 2 is the recommended next checkpoint — it removes one of the two big blockers (`ClassMeta` coupling) without yet attempting the swap/registry/session decoupling that requires reworking serializer/parser code paths. +The "incomplete-but-documented over broken-build" rule from Phase 5a still applies. When picking up the next slice of this work, Step 3 is the recommended next checkpoint — Step 2 removed the general `ClassMeta` reflection coupling, so the next blocker is the swap-aware `get`/`set` paths in `BeanPropertyMeta` (and the associated `rawTypeMeta` / `typeMeta` / `ObjectSwap` / `BeanRegistry` fields that Step 2 intentionally left in place). ---
