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 361d4cacaf6a9cf7fa90b659a400e1ed56c2dc4f Author: Alex Heneveld <[email protected]> AuthorDate: Sat Aug 14 04:38:44 2021 +0100 more object-reference deserialization tweaks --- .../core/resolve/jackson/BeanWithTypeUtils.java | 27 ++++++++++++--------- ...BrooklynRegisteredTypeJacksonSerialization.java | 5 ++-- .../jackson/ObjectReferencingSerialization.java | 28 ++++++++++++++++++---- .../util/core/internal/TypeCoercionsTest.java | 11 ++++++++- 4 files changed, 52 insertions(+), 19 deletions(-) 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 896d493..5cc11ef 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 @@ -125,7 +125,17 @@ public class BeanWithTypeUtils { */ public static <T> T convert(ManagementContext mgmt, Object mapOrListToSerializeThenDeserialize, TypeToken<T> type, boolean allowRegisteredTypes, BrooklynClassLoadingContext loader, boolean allowJavaTypes) throws JsonProcessingException { - // try with complex types are saved as objects rather than serialized + try { + return convertDeeply(mgmt, mapOrListToSerializeThenDeserialize, type, allowRegisteredTypes, loader, allowJavaTypes); + + } catch (Exception e) { + return convertShallow(mgmt, mapOrListToSerializeThenDeserialize, type, allowRegisteredTypes, loader, allowJavaTypes); + } + } + + @Beta + public static <T> T convertShallow(ManagementContext mgmt, Object mapOrListToSerializeThenDeserialize, TypeToken<T> type, boolean allowRegisteredTypes, BrooklynClassLoadingContext loader, boolean allowJavaTypes) throws JsonProcessingException { + // try with complex types are saved as objects rather than serialized, but won't work if special deserialization is wanted to apply to a map inside a complex type ObjectMapper mapper = YAMLMapper.builder().build(); mapper = BeanWithTypeUtils.applyCommonMapperConfig(mapper, mgmt, allowRegisteredTypes, loader, allowJavaTypes); mapper = new ObjectReferencingSerialization().useAndApplytoMapper(mapper); @@ -135,16 +145,11 @@ public class BeanWithTypeUtils { } @Beta - public static <T> T convertExtra(ManagementContext mgmt, Object mapOrListToSerializeThenDeserialize, TypeToken<T> type, boolean allowRegisteredTypes, BrooklynClassLoadingContext loader, boolean allowJavaTypes) throws JsonProcessingException { - try { - return convert(mgmt, mapOrListToSerializeThenDeserialize, type, allowRegisteredTypes, loader, allowJavaTypes); - - } catch (Exception e) { - // try full serialization - but won't work if things being written cannot be deserialized - ObjectMapper m = newMapper(mgmt, allowRegisteredTypes, loader, allowJavaTypes); - String serialization = m.writeValueAsString(mapOrListToSerializeThenDeserialize); - return m.readValue(serialization, BrooklynJacksonType.asJavaType(m, type)); - } + public static <T> T convertDeeply(ManagementContext mgmt, Object mapOrListToSerializeThenDeserialize, TypeToken<T> type, boolean allowRegisteredTypes, BrooklynClassLoadingContext loader, boolean allowJavaTypes) throws JsonProcessingException { + // try full serialization - but won't work if things being written cannot be deserialized, eg due to unknown type + ObjectMapper m = newMapper(mgmt, allowRegisteredTypes, loader, allowJavaTypes); + String serialization = m.writeValueAsString(mapOrListToSerializeThenDeserialize); + return m.readValue(serialization, BrooklynJacksonType.asJavaType(m, type)); } public static <T> Maybe<T> tryConvertOrAbsentUsingContext(Maybe<Object> input, TypeToken<T> type) { diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynRegisteredTypeJacksonSerialization.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynRegisteredTypeJacksonSerialization.java index b7dbad4..5351716 100644 --- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynRegisteredTypeJacksonSerialization.java +++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynRegisteredTypeJacksonSerialization.java @@ -228,10 +228,11 @@ public class BrooklynRegisteredTypeJacksonSerialization { public static ObjectMapper apply(ObjectMapper mapper, ManagementContext mgmt, boolean allowRegisteredTypes, BrooklynClassLoadingContext loader, boolean allowPojoJavaTypes) { // the type resolver is extended to recognise brooklyn registered type names // and return a subtype of jackson JavaType - mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - mapper.setDefaultTyping(new BrtTypeResolverBuilder(mgmt, allowRegisteredTypes, loader, allowPojoJavaTypes)); +// // this is tempting but it breaks places where we rely on type: ... to tell us the type +// mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + SimpleModule module = new SimpleModule(); if (allowRegisteredTypes) { module.setDeserializers(new RegisteredTypeDeserializers(mgmt)); 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 28314a3..219e110 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 @@ -37,8 +37,10 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.google.common.reflect.TypeToken; import java.io.IOException; import java.io.StringReader; +import java.lang.reflect.Type; import org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.ConfigurableBeanDeserializerModifier; import org.apache.brooklyn.util.core.flags.TypeCoercions; import org.apache.brooklyn.util.text.Identifiers; @@ -143,13 +145,29 @@ public class ObjectReferencingSerialization { @Override protected Object deserializeWrapper(JsonParser jp, DeserializationContext ctxt, BiFunctionThrowsIoException<JsonParser, DeserializationContext, Object> nestedDeserialize) throws IOException { String v = jp.getCurrentToken()== JsonToken.VALUE_STRING ? jp.getValueAsString() : null; - Class<?> expected = ctxt.getContextualType()==null ? null : ctxt.getContextualType().getRawClass(); - if (expected==null) expected = Object.class; - if (v!=null && !String.class.equals(expected)) { + if (v!=null) { + Type expected = _valueType!=null ? _valueType : _valueClass; + + // not sure if we ever need to look at contextual type + Type expected2 = ctxt.getContextualType()==null ? null : ctxt.getContextualType(); + if (expected2!=null) { + if (expected==null) { + expected = expected2; + } else { + // we have two expectations + LOG.debug("Object reference deserialization ambiguity, expected "+expected+" and "+expected2); + } + } + if (expected==null) { + expected = Object.class; + } + Object result = backingMap.get(v); if (result!=null) { - if (!expected.isInstance(result)) { - return TypeCoercions.coerce(result, expected); + // Because of how UntypedObjectDeserializer.deserializeWithType treats strings + // we cannot trust string being expected (if we could, we could exclude backing map lookup!) + if (!String.class.equals(expected)) { + result = TypeCoercions.coerce(result, TypeToken.of(expected)); } return result; } diff --git a/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java b/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java index 1a81ae9..3216736 100644 --- a/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java +++ b/core/src/test/java/org/apache/brooklyn/util/core/internal/TypeCoercionsTest.java @@ -19,6 +19,7 @@ package org.apache.brooklyn.util.core.internal; import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableList; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; @@ -425,15 +426,23 @@ public class TypeCoercionsTest { public static class ClassWithMap { Map<String,Object> properties = MutableMap.of(); + Map<String,List<MyClazz>> propsList = MutableMap.of(); } @Test public void testObjectInMapCoercion() { - ClassWithMap r1 = TypeCoercions.coerce(MutableMap.of("properties", MutableMap.of("x", 1)), ClassWithMap.class); + ClassWithMap r1 = + TypeCoercions.coerce(MutableMap.of("properties", MutableMap.of("x", 1)), ClassWithMap.class); Assert.assertEquals(r1.properties.get("x"), 1); r1 = TypeCoercions.coerce(MutableMap.of("properties", MutableMap.of("x", new MyClazz())), ClassWithMap.class); Asserts.assertInstanceOf(r1.properties.get("x"), MyClazz.class); + + r1 = TypeCoercions.coerce(MutableMap.of( + "properties", MutableMap.of("x", new MyClazz()), + "propsList", MutableMap.of("y", MutableList.of(new MyClazz())) + ), ClassWithMap.class); + Asserts.assertInstanceOf(((List)r1.propsList.get("y")).get(0), MyClazz.class); } }
