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 5b70d1bf5a315cb479031710ac083542e6d233fa Author: Alex Heneveld <[email protected]> AuthorDate: Sat Aug 14 02:17:31 2021 +0100 apply the object-reference serialization mechanism to conversion fixing the failing test, and some tidy up --- .../core/resolve/jackson/BeanWithTypeUtils.java | 24 ++++++++-- .../jackson/ObjectReferencingSerialization.java | 54 ++++++++++++++++++++-- .../BrooklynMiscJacksonSerializationTest.java | 36 +++++++++------ 3 files changed, 92 insertions(+), 22 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 cf30094..896d493 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 @@ -27,6 +27,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; +import com.google.common.annotations.Beta; import com.google.common.reflect.TypeToken; import java.util.*; import java.util.Map.Entry; @@ -124,9 +125,26 @@ public class BeanWithTypeUtils { */ public static <T> T convert(ManagementContext mgmt, Object mapOrListToSerializeThenDeserialize, TypeToken<T> type, boolean allowRegisteredTypes, BrooklynClassLoadingContext loader, boolean allowJavaTypes) throws JsonProcessingException { - ObjectMapper m = newMapper(mgmt, allowRegisteredTypes, loader, allowJavaTypes); - String serialization = m.writeValueAsString(mapOrListToSerializeThenDeserialize); - return m.readValue(serialization, BrooklynJacksonType.asJavaType(m, type)); + // try with complex types are saved as objects rather than serialized + ObjectMapper mapper = YAMLMapper.builder().build(); + mapper = BeanWithTypeUtils.applyCommonMapperConfig(mapper, mgmt, allowRegisteredTypes, loader, allowJavaTypes); + mapper = new ObjectReferencingSerialization().useAndApplytoMapper(mapper); + + String serialization = mapper.writeValueAsString(mapOrListToSerializeThenDeserialize); + return mapper.readValue(serialization, BrooklynJacksonType.asJavaType(mapper, type)); + } + + @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> Maybe<T> tryConvertOrAbsentUsingContext(Maybe<Object> input, TypeToken<T> type) { 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 465380b..28314a3 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 @@ -1,3 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package org.apache.brooklyn.core.resolve.jackson; import com.fasterxml.jackson.core.JsonGenerator; @@ -16,10 +34,13 @@ import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.BeanSerializerFactory; import com.fasterxml.jackson.databind.ser.SerializerFactory; 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 java.io.IOException; +import java.io.StringReader; import org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.ConfigurableBeanDeserializerModifier; +import org.apache.brooklyn.util.core.flags.TypeCoercions; import org.apache.brooklyn.util.text.Identifiers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,13 +52,31 @@ public class ObjectReferencingSerialization { private static final Logger LOG = LoggerFactory.getLogger(ObjectReferencingSerialization.class); - public ObjectMapper useMapper(ObjectMapper mapper) { - BiMap<String,Object> backingMap = HashBiMap.create(); + final BiMap<String,Object> backingMap = HashBiMap.create(); + ObjectMapper mapper = null; + + public Object serializeAndDeserialize(Object input) throws IOException { + return getMapper().readValue(new StringReader(getMapper().writer().writeValueAsString(input)), Object.class); + } + + public BiMap<String, Object> getBackingMap() { + return backingMap; + } + + public ObjectMapper getMapper() { + if (mapper==null) { + useAndApplytoMapper(YAMLMapper.builder().build()); + } + return mapper; + } + + public ObjectMapper useAndApplytoMapper(ObjectMapper mapper) { mapper.setSerializerFactory(ObjectReferencingSerializerFactory.extending(mapper.getSerializerFactory(), new ObjectReferenceSerializer(backingMap))); mapper = new ConfigurableBeanDeserializerModifier() .addDeserializerWrapper( d -> new ObjectReferencingJsonDeserializer(d, backingMap) ).apply(mapper); + this.mapper = mapper; return mapper; } @@ -104,9 +143,16 @@ 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; - if (v!=null) { + Class<?> expected = ctxt.getContextualType()==null ? null : ctxt.getContextualType().getRawClass(); + if (expected==null) expected = Object.class; + if (v!=null && !String.class.equals(expected)) { Object result = backingMap.get(v); - if (result!=null) return result; + if (result!=null) { + if (!expected.isInstance(result)) { + return TypeCoercions.coerce(result, expected); + } + return result; + } } return nestedDeserialize.apply(jp, ctxt); } diff --git a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/BrooklynMiscJacksonSerializationTest.java b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/BrooklynMiscJacksonSerializationTest.java index 281ad6b..83b7d29 100644 --- a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/BrooklynMiscJacksonSerializationTest.java +++ b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/BrooklynMiscJacksonSerializationTest.java @@ -21,6 +21,7 @@ package org.apache.brooklyn.core.resolve.jackson; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; import com.google.common.reflect.TypeToken; +import java.io.IOException; import java.util.Map; import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.util.collections.MutableMap; @@ -56,7 +57,7 @@ public class BrooklynMiscJacksonSerializationTest implements MapperTestFixture { Asserts.assertEquals(deser("\"1m\"", Duration.class), Duration.minutes(1)); } - static class ObjWithoutIdentityInfoAnnotation { + static class ObjForSerializingAsReference { String foo; @Override @@ -69,29 +70,34 @@ public class BrooklynMiscJacksonSerializationTest implements MapperTestFixture { @Test public void testCustomHandlerForReferences() throws Exception { - mapper = new ObjectReferencingSerialization().useMapper( - BeanWithTypeUtils.applyCommonMapperConfig( - YAMLMapper.builder() -// .handlerInstantiator(new AllBeansIdentityHandler()) - .build() - , null, false, null, true)); - - ObjWithoutIdentityInfoAnnotation f1 = new ObjWithoutIdentityInfoAnnotation(); f1.foo = "1"; - ObjWithoutIdentityInfoAnnotation f2 = new ObjWithoutIdentityInfoAnnotation(); f2.foo = "2"; + mapper = YAMLMapper.builder().build(); + mapper = BeanWithTypeUtils.applyCommonMapperConfig(mapper, null, false, null, true); + mapper = new ObjectReferencingSerialization().useAndApplytoMapper(mapper); + + ObjForSerializingAsReference f1 = new ObjForSerializingAsReference(); f1.foo = "1"; + ObjForSerializingAsReference f2 = new ObjForSerializingAsReference(); f2.foo = "2"; String out = ser(MutableMap.of("a", f1, "b", f2, "c", f1)); LOG.info("Result of "+ JavaClassNames.niceClassAndMethod()+": "+out); Map in = deser(out, -// Map.class - new TypeToken<Map<String, ObjWithoutIdentityInfoAnnotation>>() {} + Map.class +// new TypeToken<Map<String, ObjForSerializingAsReference>>() {} ); - ObjWithoutIdentityInfoAnnotation a = (ObjWithoutIdentityInfoAnnotation)in.get("a"); - ObjWithoutIdentityInfoAnnotation b = (ObjWithoutIdentityInfoAnnotation)in.get("b"); - ObjWithoutIdentityInfoAnnotation c = (ObjWithoutIdentityInfoAnnotation)in.get("c"); + ObjForSerializingAsReference a = (ObjForSerializingAsReference)in.get("a"); + ObjForSerializingAsReference b = (ObjForSerializingAsReference)in.get("b"); + ObjForSerializingAsReference c = (ObjForSerializingAsReference)in.get("c"); Asserts.assertTrue(a.foo.equals(c.foo), "expected same foo value for a and c - "+a+" != "+c); Asserts.assertTrue(!b.foo.equals(c.foo), "expected different foo value for a and b"); Asserts.assertTrue(a == c, "expected same instance for a and c - "+a+" != "+c); Asserts.assertTrue(a != b, "expected different instance for a and b"); } + @Test + public void testObjectReferences() throws IOException { + ObjForSerializingAsReference f1 = new ObjForSerializingAsReference(); f1.foo = "1"; + Object f2 = new ObjectReferencingSerialization().serializeAndDeserialize(f1); + Asserts.assertEquals(f1, f2); + Asserts.assertTrue(f1==f2, "different instances for "+f1+" and "+f2); + } + }
