From ffd7ef8f85102d01c06561b0594d5b6e99492e2e Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 25 Apr 2025 20:49:59 -0700 Subject: [PATCH 1/5] Check in skeletal test --- .../databind/misc/Reflection4907Test.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/databind/misc/Reflection4907Test.java diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/Reflection4907Test.java b/src/test/java/com/fasterxml/jackson/databind/misc/Reflection4907Test.java new file mode 100644 index 0000000000..5c05cf538a --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/misc/Reflection4907Test.java @@ -0,0 +1,54 @@ +package com.fasterxml.jackson.databind.misc; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class Reflection4907Test extends DatabindTestUtil +{ + static class SqlDatePojo { + public String name; + public java.sql.Date date; + + public SqlDatePojo() { + } + + public SqlDatePojo(String name, java.sql.Date date) { + this.name = name; + this.date = date; + } + + public SqlDatePojo(java.sql.Date date) { + this.date = date; + } + + public java.sql.Date getDate() { + return date; + } + + public void setDate(java.sql.Date date) { + this.date = date; + } + } + + private final ObjectMapper MAPPER = newJsonMapper(); + + // [databind#4907] + @Test + public void test4907Read() throws Exception { + SqlDatePojo pojo = MAPPER.readValue(a2q("{'date':'2000-01-01', 'name':'foo'}"), + SqlDatePojo.class); + assertNotNull(pojo); + } + + // [databind#4907] + @Test + public void test4907Write() throws Exception { + String json = MAPPER.writeValueAsString(new SqlDatePojo("foobar", + java.sql.Date.valueOf("2000-01-01"))); + assertNotNull(json); + } +} From a1ab4a964cfcb26adc36f6f0872993fc2c736f33 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 25 Apr 2025 21:08:55 -0700 Subject: [PATCH 2/5] ... --- .../jackson/databind/introspect/AnnotatedClass.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java index edce49b863..6f95b4a9b3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java @@ -326,8 +326,10 @@ private final List _fields() { if (f == null) { // 09-Jun-2017, tatu: _type only null for primordial, placeholder array types. if (_type == null) { +System.out.println("SKIP Collecting _fields() for ???"); f = Collections.emptyList(); } else { +System.out.println("Collecting _fields() for "+_type); f = AnnotatedFieldCollector.collectFields(_annotationIntrospector, this, _mixInResolver, _typeFactory, _type, _collectAnnotations); } @@ -342,8 +344,10 @@ private final AnnotatedMethodMap _methods() { // 09-Jun-2017, tatu: _type only null for primordial, placeholder array types. // NOTE: would be great to have light-weight shareable maps; no such impl exists for now if (_type == null) { +System.out.println("SKIP Collecting _methods() for ???"); m = new AnnotatedMethodMap(); } else { +System.out.println("Collecting _methods() for "+_type); m = AnnotatedMethodCollector.collectMethods(_annotationIntrospector, this, _mixInResolver, _typeFactory, @@ -358,8 +362,10 @@ private final Creators _creators() { Creators c = _creators; if (c == null) { if (_type == null) { +System.out.println("SKIP Collecting _creators()"); c = NO_CREATORS; } else { +System.out.println("Collecting _creators() for "+_type); c = AnnotatedCreatorCollector.collectCreators(_annotationIntrospector, _typeFactory, this, _type, _primaryMixIn, _collectAnnotations); From 0ad4bc4847a548864f3c9f993ae10dc6b2800c81 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sat, 26 Apr 2025 18:02:15 -0700 Subject: [PATCH 3/5] More debug printing --- .../fasterxml/jackson/databind/introspect/AnnotatedClass.java | 1 + .../jackson/databind/introspect/AnnotatedClassResolver.java | 2 ++ .../jackson/databind/introspect/BasicClassIntrospector.java | 4 ++++ .../fasterxml/jackson/databind/misc/Reflection4907Test.java | 4 ++++ 4 files changed, 11 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java index 6f95b4a9b3..801515acdc 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java @@ -150,6 +150,7 @@ public final class AnnotatedClass _mixInResolver = mir; _typeFactory = tf; _collectAnnotations = collectAnnotations; +System.out.println(" AnnotatedClass("+_type+"), coll anno? "+collectAnnotations); } /** diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java index 55acc9f209..8ee68f7a50 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClassResolver.java @@ -132,6 +132,8 @@ static AnnotatedClass createArrayType(MapperConfig config, Class raw) { } AnnotatedClass resolveFully() { +System.out.println(" AnnotatedClassResolver.resolveFully() for "+_type); + List superTypes = new ArrayList<>(8); if (!_type.hasRawClass(Object.class)) { if (_type.isInterface()) { diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java index 7927c39909..450dc54529 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java @@ -83,6 +83,7 @@ public BasicBeanDescription forSerialization(SerializationConfig config, // structured types as well desc = _findStdJdkCollectionDesc(config, type); if (desc == null) { +System.out.println("BasicBeanDescription.forSerialization() for "+type); desc = BasicBeanDescription.forSerialization(collectProperties(config, type, r, true)); } @@ -101,6 +102,7 @@ public BasicBeanDescription forDeserialization(DeserializationConfig config, // structured types as well desc = _findStdJdkCollectionDesc(config, type); if (desc == null) { +System.out.println("BasicBeanDescription.forDeserialization() for "+type); desc = BasicBeanDescription.forDeserialization(collectProperties(config, type, r, false)); } @@ -127,6 +129,7 @@ public BasicBeanDescription forCreation(DeserializationConfig config, // structured types as well desc = _findStdJdkCollectionDesc(config, type); if (desc == null) { +System.out.println("BasicBeanDescription.forCreation() for "+type); desc = BasicBeanDescription.forDeserialization( collectProperties(config, type, r, false)); } @@ -170,6 +173,7 @@ public BasicBeanDescription forDirectClassAnnotations(MapperConfig config, protected POJOPropertiesCollector collectProperties(MapperConfig config, JavaType type, MixInResolver r, boolean forSerialization) { +System.out.println(" collectProperties() for "+type); final AnnotatedClass classDef = _resolveAnnotatedClass(config, type, r); final AccessorNamingStrategy accNaming = type.isRecordType() ? config.getAccessorNaming().forRecord(config, classDef) diff --git a/src/test/java/com/fasterxml/jackson/databind/misc/Reflection4907Test.java b/src/test/java/com/fasterxml/jackson/databind/misc/Reflection4907Test.java index 5c05cf538a..fd690b5884 100644 --- a/src/test/java/com/fasterxml/jackson/databind/misc/Reflection4907Test.java +++ b/src/test/java/com/fasterxml/jackson/databind/misc/Reflection4907Test.java @@ -39,16 +39,20 @@ public void setDate(java.sql.Date date) { // [databind#4907] @Test public void test4907Read() throws Exception { +System.err.println(""); SqlDatePojo pojo = MAPPER.readValue(a2q("{'date':'2000-01-01', 'name':'foo'}"), SqlDatePojo.class); +System.err.println(""); assertNotNull(pojo); } // [databind#4907] @Test public void test4907Write() throws Exception { +System.err.println(""); String json = MAPPER.writeValueAsString(new SqlDatePojo("foobar", java.sql.Date.valueOf("2000-01-01"))); + System.err.println(""); assertNotNull(json); } } From ac67f6c93d42e91f2a9ac8f3e1cd80bc80d41304 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 27 Apr 2025 17:24:48 -0700 Subject: [PATCH 4/5] ... --- .../databind/introspect/AnnotatedClass.java | 45 +++++++++++++++---- .../jackson/databind/util/ClassUtil.java | 11 +++++ .../ser/jdk/JDKTypeSerializationTest.java | 4 +- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java index 801515acdc..4d349fd3f8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java @@ -77,13 +77,24 @@ public final class AnnotatedClass final protected Class _primaryMixIn; /** - * Flag that indicates whether (fulll) annotation resolution should - * occur: starting with 2.11 is disabled for JDK container types. + * Flag that indicates whether (full) annotation resolution should + * occur: starting with 2.11 is disabled for JDK container types + * {@link ClassUtil#isJDKClass(Class)} and {@code type.isContainerType()}. + * Later in 2.19 all JDK types have this disabled. * * @since 2.11 */ final protected boolean _collectAnnotations; + /** + * Flag that indicates whether member (Field, Method, Constructor} + * detection should occur: starting with 2.19 is disabled for core JDK + * types {@link ClassUtil#isJDKCoreClass(Class)}). + * + * @since 2.19 + */ + final protected boolean _collectMembers; + /* /********************************************************** /* Gathered information @@ -150,7 +161,19 @@ public final class AnnotatedClass _mixInResolver = mir; _typeFactory = tf; _collectAnnotations = collectAnnotations; -System.out.println(" AnnotatedClass("+_type+"), coll anno? "+collectAnnotations); + // But need to collect for some JDK types: + // + // - Throwables + _collectMembers = (type != null) + && (!ClassUtil.isJDKCoreClass(rawType) + || type.hasRawClass(Optional.class) + || type.hasRawClass(StackTraceElement.class) + || Throwable.class.isAssignableFrom(rawType) + || type.hasRawClass(Thread.class) + || type.hasRawClass(ThreadGroup.class) + ); + +System.out.println(" AnnotatedClass("+_type+"), coll anno? "+collectAnnotations+" coll mem? "+_collectMembers); } /** @@ -170,6 +193,7 @@ public final class AnnotatedClass _mixInResolver = null; _typeFactory = null; _collectAnnotations = false; + _collectMembers = false; } /** @@ -326,8 +350,9 @@ private final List _fields() { List f = _fields; if (f == null) { // 09-Jun-2017, tatu: _type only null for primordial, placeholder array types. - if (_type == null) { -System.out.println("SKIP Collecting _fields() for ???"); + // 26-Apr-2025, tatu: [databind#4907] Less introspection, skip for core JDK types + if (_type == null || !_collectMembers) { +System.out.println("SKIP Collecting _fields() for "+_type); f = Collections.emptyList(); } else { System.out.println("Collecting _fields() for "+_type); @@ -344,8 +369,9 @@ private final AnnotatedMethodMap _methods() { if (m == null) { // 09-Jun-2017, tatu: _type only null for primordial, placeholder array types. // NOTE: would be great to have light-weight shareable maps; no such impl exists for now - if (_type == null) { -System.out.println("SKIP Collecting _methods() for ???"); + // 26-Apr-2025, tatu: [databind#4907] Less introspection, skip for core JDK types + if (_type == null || !_collectMembers) { +System.out.println("SKIP Collecting _methods() for: "+_type); m = new AnnotatedMethodMap(); } else { System.out.println("Collecting _methods() for "+_type); @@ -362,8 +388,9 @@ private final AnnotatedMethodMap _methods() { private final Creators _creators() { Creators c = _creators; if (c == null) { - if (_type == null) { -System.out.println("SKIP Collecting _creators()"); + // 26-Apr-2025, tatu: [databind#4907] Less introspection, skip for core JDK types + if (_type == null || !_collectMembers) { +System.out.println("SKIP Collecting _creators() for: "+_type); c = NO_CREATORS; } else { System.out.println("Collecting _creators() for "+_type); diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java index 9bd5862f99..68fc0c0d5e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java @@ -1187,6 +1187,17 @@ public static boolean isJDKClass(Class rawType) { ; } + /** + * Similar to {@link #isJDKClass(Class)}, but for JDK core classes: those in + * packages under {@code java.*} but NOT under {@code javax.*} (or {@code sun.*}). + * + * @since 2.19 + */ + public static boolean isJDKCoreClass(Class rawType) { + final String clsName = rawType.getName(); + return clsName.startsWith("java."); + } + /** * Convenience method for: *
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java
index c41228d5f5..057c7621e0 100644
--- a/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/jdk/JDKTypeSerializationTest.java
@@ -241,8 +241,8 @@ public void testNonStandardProperties() throws Exception
     public void testThreadSerialization() throws Exception
     {
         final Thread input = Thread.currentThread();
-//        String json = MAPPER.writerWithDefaultPrettyPrinter()
-//                .writeValueAsString(input);
+//      System.err.println("Thread -> "+MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(input));
+      
         Map asMap = MAPPER.convertValue(input, Map.class);
 //        System.err.println("PROPS -> "+asMap.keySet());
 

From 9355c96f79d21f1d0eabf474e357ffc02ee45377 Mon Sep 17 00:00:00 2001
From: Tatu Saloranta 
Date: Fri, 2 May 2025 15:30:26 -0700
Subject: [PATCH 5/5] ...

---
 .../jackson/databind/introspect/AnnotatedClass.java        | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
index 938e11abd2..ec7e0bae12 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
@@ -91,9 +91,9 @@ public final class AnnotatedClass
      * detection should occur: starting with 2.19 is disabled for core JDK
      * types {@link ClassUtil#isJDKCoreClass(Class)}).
      *
-     * @since 2.19
+     * @since 2.20
      */
-    final protected boolean _collectMembers;
+    protected final boolean _collectMembers;
 
     /*
     /**********************************************************
@@ -369,8 +369,7 @@ private final AnnotatedMethodMap _methods() {
         if (m == null) {
             // 09-Jun-2017, tatu: _type only null for primordial, placeholder array types.
             //    NOTE: would be great to have light-weight shareable maps; no such impl exists for now
-            // 26-Apr-2025, tatu: [databind#4907] Less introspection, skip for core JDK types
-            if (_type == null || !_collectMembers) {
+            if (_type == null) { // || !_collectMembers) {
 System.out.println("SKIP Collecting _methods() for: "+_type);
                 m = new AnnotatedMethodMap();
             } else {