diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 3a1968556b..e1e109bcdf 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -1738,3 +1738,7 @@ Muhammad Khalikov (mukham12@github) * Contributed fix for #4209: Make `BeanDeserializerModifier`/`BeanSerializerModifier` implement `java.io.Serializable` (2.17.0) + +Eduard Dudar (edudar@github) + * Contributed #4299: Some `Collection` and `Map` fallbacks don't work in GraalVM native image + (2.17.0) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index b2ff000b7d..1b46d3afb6 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -31,6 +31,8 @@ Project: jackson-databind #4262: Improve handling of `null` insertion failure for `TreeSet` #4263: Change `ObjectArrayDeserializer` to use "generic" type parameter (`java.lang.Object`) to remove co-variant return type +#4299: Some `Collection` and `Map` fallbacks don't work in GraalVM native image + (contributed by Eduard D) #4309: `@JsonSetter(nulls=...)` handling of `Collection` `null` values during deserialization with `READ_UNKNOWN_ENUM_VALUES_AS_NULL` and `FAIL_ON_INVALID_SUBTYPE` wrong (reported by @ivan-zaitsev) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators.java index 1f41e4b265..4e6788fdc8 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/JDKValueInstantiators.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import com.fasterxml.jackson.core.JsonLocation; @@ -28,14 +29,23 @@ public static ValueInstantiator findStdValueInstantiator(DeserializationConfig c // [databind#1868]: empty List/Set/Map // [databind#2416]: optimize commonly needed default creators if (Collection.class.isAssignableFrom(raw)) { - if (raw == ArrayList.class) { + if (raw == ArrayList.class) { // default impl, pre-constructed instance return ArrayListInstantiator.INSTANCE; } - if (Collections.EMPTY_SET.getClass() == raw) { - return new ConstantValueInstantiator(Collections.EMPTY_SET); + if (raw == HashSet.class) { // default impl, pre-constructed instance + return HashSetInstantiator.INSTANCE; } - if (Collections.EMPTY_LIST.getClass() == raw) { - return new ConstantValueInstantiator(Collections.EMPTY_LIST); + if (raw == LinkedList.class) { + return new LinkedListInstantiator(); + } + if (raw == TreeSet.class) { + return new TreeSetInstantiator(); + } + if (raw == Collections.emptySet().getClass()) { + return new ConstantValueInstantiator(Collections.emptySet()); + } + if (raw == Collections.emptyList().getClass()) { + return new ConstantValueInstantiator(Collections.emptyList()); } } else if (Map.class.isAssignableFrom(raw)) { if (raw == LinkedHashMap.class) { @@ -44,18 +54,25 @@ public static ValueInstantiator findStdValueInstantiator(DeserializationConfig c if (raw == HashMap.class) { return HashMapInstantiator.INSTANCE; } - if (Collections.EMPTY_MAP.getClass() == raw) { - return new ConstantValueInstantiator(Collections.EMPTY_MAP); + if (raw == ConcurrentHashMap.class) { + return new ConcurrentHashMapInstantiator(); + } + if (raw == TreeMap.class) { + return new TreeMapInstantiator(); + } + if (raw == Collections.emptyMap().getClass()) { + return new ConstantValueInstantiator(Collections.emptyMap()); } } return null; } + // @since 2.17 private abstract static class JDKValueInstantiator extends ValueInstantiator.Base implements java.io.Serializable { - private static final long serialVersionUID = 2L; + private static final long serialVersionUID = 2L; public JDKValueInstantiator(Class type) { super(type); @@ -77,7 +94,8 @@ private static class ArrayListInstantiator { private static final long serialVersionUID = 2L; - public final static ArrayListInstantiator INSTANCE = new ArrayListInstantiator(); + static final ArrayListInstantiator INSTANCE = new ArrayListInstantiator(); + public ArrayListInstantiator() { super(ArrayList.class); } @@ -88,12 +106,78 @@ public Object createUsingDefault(DeserializationContext ctxt) throws IOException } } + // @since 2.17 [databind#4299] Instantiators for additional container classes + private static class LinkedListInstantiator + extends JDKValueInstantiator + { + private static final long serialVersionUID = 2L; + + public LinkedListInstantiator() { + super(LinkedList.class); + } + + @Override + public Object createUsingDefault(DeserializationContext ctxt) throws IOException { + return new LinkedList<>(); + } + } + + // @since 2.17 [databind#4299] Instantiators for additional container classes + private static class HashSetInstantiator + extends JDKValueInstantiator + { + private static final long serialVersionUID = 2L; + + static final HashSetInstantiator INSTANCE = new HashSetInstantiator(); + + public HashSetInstantiator() { + super(HashSet.class); + } + + @Override + public Object createUsingDefault(DeserializationContext ctxt) throws IOException { + return new HashSet<>(); + } + } + + // @since 2.17 [databind#4299] Instantiators for additional container classes + private static class TreeSetInstantiator + extends JDKValueInstantiator + { + private static final long serialVersionUID = 2L; + + public TreeSetInstantiator() { + super(TreeSet.class); + } + + @Override + public Object createUsingDefault(DeserializationContext ctxt) throws IOException { + return new TreeSet<>(); + } + } + + // @since 2.17 [databind#4299] Instantiators for additional container classes + private static class ConcurrentHashMapInstantiator + extends JDKValueInstantiator + { + private static final long serialVersionUID = 2L; + + public ConcurrentHashMapInstantiator() { + super(ConcurrentHashMap.class); + } + + @Override + public Object createUsingDefault(DeserializationContext ctxt) throws IOException { + return new ConcurrentHashMap<>(); + } + } + private static class HashMapInstantiator extends JDKValueInstantiator { private static final long serialVersionUID = 2L; - public final static HashMapInstantiator INSTANCE = new HashMapInstantiator(); + static final HashMapInstantiator INSTANCE = new HashMapInstantiator(); public HashMapInstantiator() { super(HashMap.class); @@ -110,7 +194,7 @@ private static class LinkedHashMapInstantiator { private static final long serialVersionUID = 2L; - public final static LinkedHashMapInstantiator INSTANCE = new LinkedHashMapInstantiator(); + static final LinkedHashMapInstantiator INSTANCE = new LinkedHashMapInstantiator(); public LinkedHashMapInstantiator() { super(LinkedHashMap.class); @@ -122,6 +206,22 @@ public Object createUsingDefault(DeserializationContext ctxt) throws IOException } } + // @since 2.17 [databind#4299] Instantiators for additional container classes + private static class TreeMapInstantiator + extends JDKValueInstantiator + { + private static final long serialVersionUID = 2L; + + public TreeMapInstantiator() { + super(TreeMap.class); + } + + @Override + public Object createUsingDefault(DeserializationContext ctxt) throws IOException { + return new TreeMap<>(); + } + } + private static class ConstantValueInstantiator extends JDKValueInstantiator { @@ -135,7 +235,7 @@ public ConstantValueInstantiator(Object value) { } @Override - public Object createUsingDefault(DeserializationContext ctxt) throws IOException { + public final Object createUsingDefault(DeserializationContext ctxt) throws IOException { return _value; } }