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

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit c8e7bbbedd729932c137b8fe755bdb96acf16849
Author: Alex Heneveld <a...@cloudsoft.io>
AuthorDate: Fri May 10 15:09:02 2024 +0100

    apply type deserialization even into maps and lists by default
---
 .../brooklyn/spi/dsl/DslSerializationTest.java     |  6 ++-
 .../core/resolve/jackson/BeanWithTypeUtils.java    | 12 ++++-
 .../jackson/BrooklynJacksonSerializationUtils.java | 63 ++++++++++++++++++++++
 .../jackson/ObjectReferencingSerialization.java    |  2 +-
 .../resolve/jackson/PerverseSerializationTest.java | 14 +++--
 5 files changed, 89 insertions(+), 8 deletions(-)

diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java
index f331bc588e..a71b79ccad 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java
@@ -29,9 +29,12 @@ import 
org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent;
+import 
org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.DslConfigSupplier;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
 import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
 import org.apache.brooklyn.core.resolve.jackson.*;
+import 
org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.ConfigurableBeanDeserializerModifier;
+import 
org.apache.brooklyn.core.resolve.jackson.BrooklynRegisteredTypeJacksonSerializationTest.SampleBean;
 import 
org.apache.brooklyn.core.resolve.jackson.WrappedValuesSerializationTest.ObjectWithWrappedValueString;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.core.workflow.WorkflowBasicTest;
@@ -77,7 +80,8 @@ public class DslSerializationTest extends AbstractYamlTest 
implements MapperTest
 
         Object stuff2 = mapper.readValue(out, Object.class);
         Object stuff2I = ((Map<?, ?>) stuff2).get("stuff");
-        Asserts.assertInstanceOf(stuff2I, Map.class);
+        Class<?> expectedDeserializedTypedMapNested = 
ConfigurableBeanDeserializerModifier.DEFAULT_APPLY_ONLY_TO_BEAN_DESERIALIZERS ? 
Map.class : DslConfigSupplier.class;
+        Asserts.assertInstanceOf(stuff2I, expectedDeserializedTypedMapNested);
     }
 
     private ObjectMapper newMapper() {
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java
 
b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java
index 2c85657c93..5df6c020f9 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java
@@ -164,6 +164,7 @@ public class BeanWithTypeUtils {
         // so the former calls the latter, and the latter calls the former. 
but stop at some point!
         if (stack.contains(mapOrListToSerializeThenDeserialize)) throw new 
IllegalStateException("Aborting recursive attempt to convert 
'"+mapOrListToSerializeThenDeserialize+"'");
 
+        T wrongTypeResult = null;
         try {
             stack.push(mapOrListToSerializeThenDeserialize);
 
@@ -184,14 +185,21 @@ public class BeanWithTypeUtils {
 //                return result2;
 //            }
 
-            return result;
+            if (result!=null && !type.getRawType().isInstance(result)) {
+                wrongTypeResult = result;
+                throw new IllegalStateException("Wrong type returned");  // 
will be ignored below
+            } else {
+                return result;
+            }
 
         } catch (Exception e) {
             try {
                 // needed for a few things, mainly where a bean has a type 
field that conflicts with the type here,
-                // tryCoercer 81-wrong-bean uses this
+                // tryCoercer -20-wrong-bean uses this
                 return convertDeeply(mgmt, 
mapOrListToSerializeThenDeserialize, type, allowRegisteredTypes, loader, 
allowJavaTypes);
             } catch (Exception e2) {
+                Exceptions.propagateIfFatal(e2);
+                if (wrongTypeResult!=null) return wrongTypeResult;
                 throw Exceptions.propagate(Arrays.asList(e, e2));
             }
         } finally {
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynJacksonSerializationUtils.java
 
b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynJacksonSerializationUtils.java
index 4134a718c3..e957c28cfe 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynJacksonSerializationUtils.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynJacksonSerializationUtils.java
@@ -28,6 +28,12 @@ import 
com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
 import com.fasterxml.jackson.databind.deser.DeserializerFactory;
 import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
 import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.type.ArrayType;
+import com.fasterxml.jackson.databind.type.CollectionLikeType;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.fasterxml.jackson.databind.type.ReferenceType;
 import com.fasterxml.jackson.databind.util.TokenBuffer;
 import com.google.common.annotations.Beta;
 
@@ -126,8 +132,11 @@ public class BrooklynJacksonSerializationUtils {
     }
 
     public static class ConfigurableBeanDeserializerModifier extends 
BeanDeserializerModifier {
+        public static boolean DEFAULT_APPLY_ONLY_TO_BEAN_DESERIALIZERS = false;
+
         List<Function<JsonDeserializer<?>,JsonDeserializer<?>>> 
deserializerWrappers = MutableList.of();
         List<Function<Object,Object>> postConstructFunctions = 
MutableList.of();
+        public boolean applyOnlyToBeanDeserializers = 
DEFAULT_APPLY_ONLY_TO_BEAN_DESERIALIZERS;
 
         public ConfigurableBeanDeserializerModifier 
addDeserializerWrapper(Function<JsonDeserializer<?>,JsonDeserializer<?>> ...f) {
             for (Function<JsonDeserializer<?>,JsonDeserializer<?>> fi: f) 
deserializerWrappers.add(fi);
@@ -142,6 +151,10 @@ public class BrooklynJacksonSerializationUtils {
         public JsonDeserializer<?> modifyDeserializer(DeserializationConfig 
config,
                                                       BeanDescription beanDesc,
                                                       JsonDeserializer<?> 
deserializer) {
+            return applyOurModifiers(deserializer);
+        }
+
+        protected JsonDeserializer<?> applyOurModifiers(JsonDeserializer<?> 
deserializer) {
             for (Function<JsonDeserializer<?>,JsonDeserializer<?>> d: 
deserializerWrappers) {
                 deserializer = d.apply(deserializer);
             }
@@ -151,6 +164,56 @@ public class BrooklynJacksonSerializationUtils {
             return deserializer;
         }
 
+        private JsonDeserializer<?> 
applyOurModifiersNonBean(JsonDeserializer<?> deserializer) {
+            if (applyOnlyToBeanDeserializers) {
+                // all the super's callers just return the input, so it is 
safe to do this, ignoring the supers of the calling method
+                return deserializer;
+            }
+            return applyOurModifiers(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> modifyMapDeserializer(DeserializationConfig 
config, MapType type, BeanDescription beanDesc, JsonDeserializer<?> 
deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> 
modifyArrayDeserializer(DeserializationConfig config, ArrayType valueType, 
BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> 
modifyCollectionDeserializer(DeserializationConfig config, CollectionType type, 
BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> 
modifyCollectionLikeDeserializer(DeserializationConfig config, 
CollectionLikeType type, BeanDescription beanDesc, JsonDeserializer<?> 
deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> 
modifyEnumDeserializer(DeserializationConfig config, JavaType type, 
BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> 
modifyMapLikeDeserializer(DeserializationConfig config, MapLikeType type, 
BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> 
modifyReferenceDeserializer(DeserializationConfig config, ReferenceType type, 
BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        // not supported at key because it is a different hierarchy; our 
deserializers probably don't apply; no known use case so NBD
+//        @Override
+//        public KeyDeserializer modifyKeyDeserializer(DeserializationConfig 
config, JavaType type, KeyDeserializer deserializer) {
+//            // super.modifyKeyDeserializer(config, type, deserializer);
+//            return applyOurModifiersNonBean(deserializer);
+//        }
+
         public <T extends ObjectMapper> T apply(T mapper) {
             SimpleModule module = new SimpleModule();
             module.setDeserializerModifier(this);
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
 
b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
index 6ad6990ec4..8c0bfa6a52 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
@@ -178,7 +178,7 @@ public class ObjectReferencingSerialization {
                         // and if the expected type is overly strict, return 
the original object.
                         // if the token buffer is used too much we might have 
lost the alias reference and still end up with a string,
                         // but so long as this deserializer is preferred which 
it normally is, losing the alias reference is okay.
-                        Maybe resultCoerced = ((Maybe) 
TypeCoercions.tryCoerce(result, TypeToken.of(expected)));
+                        Maybe resultCoerced = TypeCoercions.tryCoerce(result, 
TypeToken.of(expected));
                         if (resultCoerced.isAbsent()) {
                             // not uncommon when we are trying to deserialize 
in a few different ways, or if we are using a string deserializer because the 
json input is a string;
                             // however it can be useful, to explain why some 
things aren't coercing via BeanWithTypeUtils.convert.
diff --git 
a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/PerverseSerializationTest.java
 
b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/PerverseSerializationTest.java
index 2c543698f6..cce46facaf 100644
--- 
a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/PerverseSerializationTest.java
+++ 
b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/PerverseSerializationTest.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.core.resolve.jackson;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.Iterables;
 import javassist.tools.rmi.Sample;
+import 
org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.ConfigurableBeanDeserializerModifier;
 import 
org.apache.brooklyn.core.resolve.jackson.BrooklynRegisteredTypeJacksonSerializationTest.SampleBean;
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.collections.MutableList;
@@ -96,9 +97,11 @@ public class PerverseSerializationTest implements 
MapperTestFixture {
 
         // here, if it's an object we are reading, we get a list containing a 
map
         x = deser(LIST_MAP_TYPE_SAMPLE_BEAN, Object.class);
-        Asserts.assertInstanceOf(Iterables.getOnlyElement((List) x), 
Map.class);
+        // previously we did not deserialize types inside maps, but now we do
+        Class<?> expectedDeserializedTypedMapNested = 
ConfigurableBeanDeserializerModifier.DEFAULT_APPLY_ONLY_TO_BEAN_DESERIALIZERS ? 
Map.class : SampleBean.class;
+        Asserts.assertInstanceOf(Iterables.getOnlyElement((List) x), 
expectedDeserializedTypedMapNested);
 
-        // but if we know it's a list then we are more aggressive about 
reading type information
+        // if we know it's a list then we are more aggressive about reading 
type information, we do not need our bean deserializer to modify non-bean 
deserializers
         x = deser(LIST_MAP_TYPE_SAMPLE_BEAN, List.class);
         Asserts.assertInstanceOf(Iterables.getOnlyElement((List) x), 
SampleBean.class);
     }
@@ -122,8 +125,11 @@ public class PerverseSerializationTest implements 
MapperTestFixture {
         Asserts.assertInstanceOf(Iterables.getOnlyElement(o.ls), 
SampleBean.class);
         Asserts.assertInstanceOf(Iterables.getOnlyElement(o.lo), 
SampleBean.class);
         Asserts.assertInstanceOf(Iterables.getOnlyElement(o.lr), 
SampleBean.class);
-        Asserts.assertInstanceOf(Iterables.getOnlyElement((List)o.o), 
Map.class);
-        
Asserts.assertInstanceOf(Iterables.getOnlyElement((List)Iterables.getOnlyElement(o.lo2)),
 Map.class);
+
+        // previously we did not deserialize types inside maps, but now we do
+        Class<?> expectedDeserializedTypedMapNested = 
ConfigurableBeanDeserializerModifier.DEFAULT_APPLY_ONLY_TO_BEAN_DESERIALIZERS ? 
Map.class : SampleBean.class;
+        Asserts.assertInstanceOf(Iterables.getOnlyElement((List)o.o), 
expectedDeserializedTypedMapNested);
+        
Asserts.assertInstanceOf(Iterables.getOnlyElement((List)Iterables.getOnlyElement(o.lo2)),
 expectedDeserializedTypedMapNested);
     }
 
     /* arrays - no surprises - always read and written as lists, and coerced 
if context requires */

Reply via email to