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 2601ea0f666f899e607a0435dc3c48c148691f73
Author: Alex Heneveld <[email protected]>
AuthorDate: Wed Jan 19 17:02:48 2022 +0000

    make Mutable{list,set,map} more robust in the face of concurrent 
modifications
    
    simplify list format, and make robust to allow legacy format also
---
 .../util/core/xstream/MutableListConverter.java    | 77 ++++++++++++++++++++++
 .../util/core/xstream/MutableSetConverter.java     | 12 ++++
 .../util/core/xstream/StringKeyMapConverter.java   | 21 +++---
 .../brooklyn/util/core/xstream/XmlSerializer.java  |  3 +-
 .../util/core/xstream/XmlSerializerTest.java       | 25 ++++---
 5 files changed, 119 insertions(+), 19 deletions(-)

diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/MutableListConverter.java
 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/MutableListConverter.java
new file mode 100644
index 0000000..002859b
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/MutableListConverter.java
@@ -0,0 +1,77 @@
+/*
+ * 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.util.core.xstream;
+
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
+import com.thoughtworks.xstream.converters.reflection.SerializableConverter;
+import com.thoughtworks.xstream.core.ClassLoaderReference;
+import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.mapper.MapperWrapper;
+import org.apache.brooklyn.util.collections.MutableList;
+
+import com.thoughtworks.xstream.converters.collections.CollectionConverter;
+import com.thoughtworks.xstream.mapper.Mapper;
+
+public class MutableListConverter extends CollectionConverter {
+
+    final SerializableConverter serializableConverterTweakedForDeserializing;
+
+    public MutableListConverter(Mapper mapper, ReflectionProvider 
reflectionProvider, ClassLoaderReference classLoaderReference) {
+        super(mapper);
+        serializableConverterTweakedForDeserializing = new 
SerializableConverter(new MapperWrapper(mapper) {
+            @Override
+            public String aliasForSystemAttribute(String attribute) {
+                // don't expect "serialization=custom" as an attribute on our 
node; always fall back to custom
+                if ("serialization".equals(attribute)) return null;
+                return super.aliasForSystemAttribute(attribute);
+            }
+        }, reflectionProvider, classLoaderReference);
+    }
+
+    @Override
+    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
+        return MutableList.class.isAssignableFrom(type);
+    }
+
+    @Override
+    protected Object createCollection(Class type) {
+        return new MutableList<Object>();
+    }
+
+    // take a copy first to avoid CMEs
+    @Override
+    public void marshal(Object source, HierarchicalStreamWriter writer, 
MarshallingContext context) {
+        super.marshal(MutableList.copyOf((MutableList)source), writer, 
context);
+    }
+
+    @Override
+    public Object unmarshal(HierarchicalStreamReader reader, 
UnmarshallingContext context) {
+        // support legacy format (when SerializableConverter wrote this, not 
us)
+        boolean legacy = 
"unserializable-parents".equals(((ExtendedHierarchicalStreamReader)reader).peekNextChild());
+        if (legacy) {
+            return 
serializableConverterTweakedForDeserializing.unmarshal(reader, context);
+        }
+
+        return super.unmarshal(reader, context);
+    }
+}
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/MutableSetConverter.java
 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/MutableSetConverter.java
index c1e6bf4..46032d6 100644
--- 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/MutableSetConverter.java
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/MutableSetConverter.java
@@ -18,6 +18,11 @@
  */
 package org.apache.brooklyn.util.core.xstream;
 
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.converters.reflection.SerializableConverter;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
 import org.apache.brooklyn.util.collections.MutableSet;
 
 import com.thoughtworks.xstream.converters.collections.CollectionConverter;
@@ -41,4 +46,11 @@ public class MutableSetConverter extends CollectionConverter 
{
     protected Object createCollection(Class type) {
         return new MutableSet<Object>();
     }
+
+    // take a copy first to avoid CMEs
+    @Override
+    public void marshal(Object source, HierarchicalStreamWriter writer, 
MarshallingContext context) {
+        super.marshal(MutableSet.copyOf((MutableSet)source), writer, context);
+    }
+
 }
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/StringKeyMapConverter.java
 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/StringKeyMapConverter.java
index dbc0a05..45ce2b2 100644
--- 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/StringKeyMapConverter.java
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/StringKeyMapConverter.java
@@ -18,9 +18,14 @@
  */
 package org.apache.brooklyn.util.core.xstream;
 
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.mapper.Mapper;
 import java.util.Map;
 import java.util.Map.Entry;
-
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.exceptions.Exceptions;
@@ -28,13 +33,6 @@ import org.apache.brooklyn.util.text.Identifiers;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.thoughtworks.xstream.converters.MarshallingContext;
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
-import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
-import com.thoughtworks.xstream.io.HierarchicalStreamReader;
-import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
-import com.thoughtworks.xstream.mapper.Mapper;
-
 /** converter which simplifies representation of a map for string-based keys,
  * to <key>value</key>, or <entry key="key" type="string">value</entry> 
  * @author alex
@@ -63,6 +61,13 @@ public class StringKeyMapConverter extends MapConverter {
     public boolean canConvert(Class type) {
         return super.canConvert(type) || 
type.getName().equals(MutableMap.class.getName());
     }
+
+    @Override
+    @SuppressWarnings({ "rawtypes" })
+    public void marshal(Object source, HierarchicalStreamWriter writer, 
MarshallingContext context) {
+        // marshall a copy to minimise CMEs
+        super.marshal(MutableMap.copyOf((Map) source), writer, context);
+    }
     
     @Override
     protected void marshalEntry(HierarchicalStreamWriter writer, 
MarshallingContext context, Entry entry) {
diff --git 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/XmlSerializer.java 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/XmlSerializer.java
index e7ce42a..ace24c7 100644
--- 
a/core/src/main/java/org/apache/brooklyn/util/core/xstream/XmlSerializer.java
+++ 
b/core/src/main/java/org/apache/brooklyn/util/core/xstream/XmlSerializer.java
@@ -107,7 +107,8 @@ public class XmlSerializer<T> {
         // Without it, the alias for "set" seems to interfere with the 
MutableSet.map field, so it gets
         // a null field on deserialization.
         xstream.registerConverter(new 
MutableSetConverter(xstream.getMapper()));
-        
+        xstream.registerConverter(new 
MutableListConverter(xstream.getMapper(), xstream.getReflectionProvider(), 
xstream.getClassLoaderReference()));
+
         xstream.aliasType("ImmutableList", ImmutableList.class);
         xstream.registerConverter(new 
ImmutableListConverter(xstream.getMapper()));
         xstream.registerConverter(new 
ImmutableSetConverter(xstream.getMapper()));
diff --git 
a/core/src/test/java/org/apache/brooklyn/util/core/xstream/XmlSerializerTest.java
 
b/core/src/test/java/org/apache/brooklyn/util/core/xstream/XmlSerializerTest.java
index 52dbd14..df62549 100644
--- 
a/core/src/test/java/org/apache/brooklyn/util/core/xstream/XmlSerializerTest.java
+++ 
b/core/src/test/java/org/apache/brooklyn/util/core/xstream/XmlSerializerTest.java
@@ -79,16 +79,21 @@ public class XmlSerializerTest {
 
         // no nice serializer for this yet
         Asserts.assertEquals(serializer.toString(MutableList.of(1)),
-                "<MutableList serialization=\"custom\">\n" +
-                "  <unserializable-parents/>\n" +
-                "  <list>\n" +
-                "    <default>\n" +
-                "      <size>1</size>\n" +
-                "    </default>\n" +
-                "    <int>1</int>\n" +
-                "    <int>1</int>\n" +
-                "  </list>\n" +
-                "</MutableList>");
+                "<MutableList>\n" +
+                "  <int>1</int>\n" +
+                "</MutableList>"
+                // old (also accepted as input)
+//                "<MutableList serialization=\"custom\">\n" +
+//                "  <unserializable-parents/>\n" +
+//                "  <list>\n" +
+//                "    <default>\n" +
+//                "      <size>1</size>\n" +
+//                "    </default>\n" +
+//                "    <int>1</int>\n" +
+//                "    <int>1</int>\n" +
+//                "  </list>\n" +
+//                "</MutableList>"
+                );
     }
     
     @Test

Reply via email to