First off: I know there are many related posts in SO about this but one that I could find was able to help in my case.
So, what I'm doing is I got a very simple parent object that may have multiple child objects. Both objects comply to the java beans specifications (no-args constructor, setter and getter for all variables).
There may be multiple parents, which are saved in a repository class like this:
private final Map<String, Parent> parentItems = new ConcurrentHashMap<String, Parent>();
Every time a new parent is created, a repository class saves the parent list which is build from the parentItems
list like this:
public List<Parent> getParents() {
return new ArrayList<Parent>(parentItems.values());
}
The save operation is done by saving the List
into an xml file using XMLEncoder
. It looks like this:
public void saveParents(OutputStream os) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
XMLEncoder encoder = null;
try {
Thread.currentThread().setContextClassLoader(Parent.class.getClassLoader());
encoder = new XMLEncoder(os);
encoder.writeObject(getParents());
encoder.flush();
} finally {
if (encoder != null) {
encoder.close();
}
Thread.currentThread().setContextClassLoader(cl);
}
}
This all works fine. Now I want to add some children to the parent object and just add new child objects to the child List and rerun saveParents()
. Here is where my trouble begins:
If I use a ArrayList
within the parent to save the children, like this:
private ArrayList<Child> children = new ArrayList<Child>();
Everything works. The objects will be saved within the parent in the xml file. It looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_45" class="java.beans.XMLDecoder">
<object class="java.util.ArrayList">
<void method="add">
<object class="org.mypackage.Parent" id="Parent0">
<void property="children">
<void method="add">
<object class="org.mypackage.Child">
<void property="displayName">
<string>Child1</string>
</void>
<void property="id">
<string>myid1</string>
</void>
<void property="parent">
<object idref="Parent0"/>
</void>
</object>
</void>
<void method="add">
<object class="org.mypackage.Child">
<void property="displayName">
<string>Child2</string>
</void>
<void property="id">
<string>myid2</string>
</void>
<void property="parent">
<object idref="Parent0"/>
</void>
</object>
</void>
</void>
<void property="displayName">
<string>Parent1</string>
</void>
<void property="id">
<string>myid</string>
</void>
</object>
</void>
</object>
</java>
However: we know that ArrayList
is not thread-safe and I know that the parent object may be altered by multiple people. So how to fix that? Use a synchronizedList(...)
- right? Something like ...
private List<Child> children = Collections.synchronizedList(new ArrayList<Child>());
... then just quickly change the getter and setter to List
instead of ArrayList
and we should be fine. Okay that should work - but guess what: it fails :-(
Every time I add a child object (and run saveParent()
after that) I get a StackOverflowException
. If I look up the XML I see something like this:
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_45" class="java.beans.XMLDecoder">
<void class="java.util.ArrayList">
<void method="add">
<object class="org.mypackage.Child" id="Child0">
<void property="displayName">
<string>Child1</string>
</void>
<void property="id">
<string>myid</string>
</void>
<void property="parent">
<object class="org.mypackage.Parent"/>
</void>
</object>
</void>
</void>
<void class="java.util.ArrayList">
<void method="add">
<object idref="Child0"/>
</void>
</void>
<void class="java.util.ArrayList">
<void method="add">
<object idref="Child0"/>
</void>
</void>
<void class="java.util.ArrayList">
<void method="add">
<object idref="Child0"/>
</void>
</void>
...... AND THIS GOES ON AND ON AND ON .......
Well, guess I know where that stack overflow came from... but why? It should be serializeable but well, look at it...
How do I get a serializable, thread-safe List
(or whatever other datatype that would fit) that doesn't blow up?
Maybe any workaround you could think about in order to make that ArrayList
(that works in this scenario) thread-safe? maybe by making the whole parent object thread-safe? (however: this may have other side-effects that's why simply getting that synchronizedList(...)
to work would be the most elegant way)