You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2021/08/14 01:54:56 UTC

[brooklyn-server] 04/06: apply the object-reference serialization mechanism to conversion

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 <al...@cloudsoftcorp.com>
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);
+    }
+
 }