Issue Details (XML | Word | Printable)

Key: HUDSON-5776
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Minor Minor
Assignee: mindless
Reporter: mdillon
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
Hudson

CopyOnWriteMap.Tree cannot be serialized if it is empty

Created: 26/Feb/10 11:42 AM   Updated: 27/Feb/10 12:19 PM   Resolved: 27/Feb/10 12:19 PM
Component/s: core
Affects Version/s: None
Fix Version/s: None

File Attachments: 1. File CopyOnWriteMap.diff (3 kB)



 Description  « Hide

Under some circumstances, the "core" field of a hudson.util.CopyOnWriteMap.Tree instance can be an instance of java.util.Collections.EmptyMap, which is not a subclass of TreeMap. When this happens, the following error is seen during serialization:

2010-02-25 00:43:09,238 ERROR [hudson.model.Executor] Executor throw an exception unexpectedly
java.lang.RuntimeException: Failed to serialize com.example.MyBuild#detailsMap for class com.example.MyBuild
        at hudson.util.RobustReflectionConverter$2.writeField(RobustReflectionConverter.java:160)
        at hudson.util.RobustReflectionConverter$2.visit(RobustReflectionConverter.java:131)
        at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.visitSerializableFields(PureJavaReflectionProvider.java:130)
        at hudson.util.RobustReflectionConverter.doMarshal(RobustReflectionConverter.java:116)
        at hudson.util.RobustReflectionConverter.marshal(RobustReflectionConverter.java:89)
        at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:68)
        at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:78)
        at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:63)
        at com.thoughtworks.xstream.core.TreeMarshaller.start(TreeMarshaller.java:98)
        at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.marshal(AbstractTreeMarshallingStrategy.java:38)
        at com.thoughtworks.xstream.XStream.marshal(XStream.java:837)
        at com.thoughtworks.xstream.XStream.marshal(XStream.java:826)
        at com.thoughtworks.xstream.XStream.toXML(XStream.java:801)
        at hudson.XmlFile.write(XmlFile.java:161)
        at hudson.model.Run.save(Run.java:1235)
        at hudson.model.Run.run(Run.java:1162)
        at hudson.model.Build.run(Build.java:74)
        at hudson.model.ResourceController.execute(ResourceController.java:93)
        at hudson.model.Executor.run(Executor.java:122)
Caused by: java.lang.ClassCastException: java.util.Collections$EmptyMap cannot be cast to java.util.TreeMap
        at com.thoughtworks.xstream.converters.collections.TreeMapConverter.marshal(TreeMapConverter.java:50)
        at hudson.util.CopyOnWriteMap$Tree$ConverterImpl.marshal(CopyOnWriteMap.java:221)
        at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:68)
        at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:78)
        at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:63)
        at hudson.util.RobustReflectionConverter.marshallField(RobustReflectionConverter.java:168)
        at hudson.util.RobustReflectionConverter$2.writeField(RobustReflectionConverter.java:156)
        ... 18 more

It looks like this can happen either when the Tree() or Tree(Comparator) constructors are called or when clear() is called. In those cases, if you don't add any elements, you'll get the wrong kind of map in the core and serialization will fail. The one case that should work is if you do putAll() with an empty Map after creation; that will successfully replace the core with a TreeMap.

I've attached a proposed patch. It adds an emptyMap() method to CopyOnWriteMap that is implemented by the two subclasses. All calls that previously went to Collections.emptyMap() now let the subclass decide what type of map to return. It also adds a test for serializing an empty CopyOnWriteMap.Tree. This patch takes the naive approach of creating a new TreeMap every time that emptyMap() is called. It may or not be worthwhile to try to reuse a TreeMap instance for this purpose since CopyOnWriteMap guarantees that the core itself won't be updated.



Sort Order: Ascending order - Click to sort in descending order
scm_issue_link added a comment - 27/Feb/10 12:19 PM

Code changed in hudson
User: : mindless
Path:
trunk/hudson/main/core/src/main/java/hudson/util/CopyOnWriteMap.java
trunk/hudson/main/core/src/test/java/hudson/util/CopyOnWriteListTest.java
trunk/hudson/main/core/src/test/java/hudson/util/CopyOnWriteMapTest.java
trunk/www/changelog.html
http://hudson-ci.org/commit/28026
Log:
[FIXED HUDSON-5776] fix serialization problem with empty CopyOnWriteMap.Tree,
also add/update tests for CopyOnWriteMap and CopyOnWriteList.