Assistance Needed for XStream Conversion Issue with Policy Object - Invalid Reference Error

23 views
Skip to first unread message

Rani Agrawal

unread,
Nov 14, 2024, 4:58:02 AM11/14/24
to XStream User
  Dear Support Team,

I am encountering an error while converting a Policy object into XML using XStream, and I require assistance to resolve it. The conversion process fails with an Invalid reference error. Below are the error details:

Error Stack Trace:

  com.csc.fw.util.FwException: : com.thoughtworks.xstream.converters.ConversionException: Invalid reference ---- Debugging information ---- Message : Invalid reference Reference : ../../storedSnapshot/com.csc.pt.svc.data.relational.entity.ClientNameBasicContract Referenced-type : com.csc.pt.svc.data.relational.entity.ClientNameBasicContract Referenceable : true Class : java.util.HashSet Required-type : java.util.HashSet Converter-type : com.thoughtworks.xstream.converters.collections.CollectionConverter Path : /com.csc.pt.svc.data.relational.entity.Policy/clientNameBasicContract/set/com.csc.pt.svc.data.relational.entity.ClientNameBasicContract Line number : 1 Class[1] : org.hibernate.collection.spi.PersistentSet Required-type[1] : org.hibernate.collection.spi.PersistentSet Converter-type[1] : com.thoughtworks.xstream.converters.reflection.ReflectionConverter Class[2] : com.csc.pt.svc.data.relational.entity.Policy Required-type[2] : com.csc.pt.svc.data.relational.entity.Policy Version : Not available


Potential Cause: The error appears related to the clientNameBasicContract node, specifically with the PersistentSet collection type used by Hibernate. It seems that XStream may be struggling with the PersistentSet conversion, particularly when referencing stored snapshots.


<clientNameBasicContract class="org.hibernate.collection.spi.PersistentSet">
    <isTempSession>false</isTempSession>
    <initialized>true</initialized>
    <owner class="com.csc.pt.svc.data.relational.entity.Policy" reference="../.."/>
    <cachedSize>-1</cachedSize>
    <role>com.csc.pt.svc.data.relational.entity.Policy.clientNameBasicContract</role>
    <key class="com.csc.pt.svc.data.relational.id.PolicyID" reference="../../id"/>
    <dirty>false</dirty>
    <elementRemoved>false</elementRemoved>
    <storedSnapshot class="map">
        <com.csc.pt.svc.data.relational.entity.ClientNameBasicContract>
            <cltSeqNum>192</cltSeqNum>
            <addrSeqNum>1</addrSeqNum>
            <role>INSURED</role>
            <recProg>PMSIPP05</recProg>
            <recUser>dbo</recUser>
            <recDate>2024-11-11</recDate>
            <recTime>03:03:07</recTime>
            <id>
                <policyLocation>00</policyLocation>
                <policyMasterCompany>05</policyMasterCompany>
                <policySymbol>WCV</policySymbol>
                <policyNum>4343485</policyNum>
                <policyModule>00</policyModule>
            </id>
            <policy reference="../../../.."/>
        </com.csc.pt.svc.data.relational.entity.ClientNameBasicContract>
    </storedSnapshot>
    <allowLoadOutsideTransaction>false</allowLoadOutsideTransaction>
    <set>
        <com.csc.pt.svc.data.relational.entity.ClientNameBasicContract reference="../../storedSnapshot/com.csc.pt.svc.data.relational.entity.ClientNameBasicContract"/>
    </set>
</clientNameBasicContract>

Environment Details:

  • Hibernate version: 6.5.2
  • XStream version: [Version here]
  • Java version: 17

Could you please provide guidance on how to handle this reference error? Specifically, I’m interested in understanding:

  • If a specific configuration or XStream version would avoid these reference issues.
  • Any suggestions on customizing the conversion to handle PersistentSet or storedSnapshot differently.
  • Whether removing or altering the <set> node is advisable in this context.

Thank you for your assistance.  

Jörg Schaible

unread,
Nov 17, 2024, 5:35:54 PM11/17/24
to XStream User

On Thursday, 14. November 2024, 10:58:02 CET Rani Agrawal wrote:

 

  Dear Support Team,

I am encountering an error while converting a Policy object into XML using XStream, and I require assistance to resolve it. The conversion process fails with an Invalid reference error. Below are the error details:

[snip]

Environment Details:

  • Hibernate version: 6.5.2
  • XStream version: [Version here]
  • Java version: 17

Could you please provide guidance on how to handle this reference error? Specifically, I’m interested in understanding:

  • If a specific configuration or XStream version would avoid these reference issues.
  • Any suggestions on customizing the conversion to handle PersistentSet or storedSnapshot differently.
  • Whether removing or altering the <set> node is advisable in this context.

Thank you for your assistance.  


XStream 1.4.x does not recognize the new package of the PersistentSet in Hibernate 6.x. See the open pull request #346 for support. Currently you will have to build a private version of XStream, sorry. I am travelling next week and won't be available.


Regards,

Jörg

Elias David

unread,
Dec 4, 2024, 8:44:06 AM12/4/24
to xstrea...@googlegroups.com
First time I encounter something like this:

java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
    at com.thoughtworks.xstream.converters.collections.CollectionConverter.marshal(CollectionConverter.java:74)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:68)
    at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:59)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller$1.convertAnother(AbstractReferenceMarshaller.java:83)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshallField(AbstractReflectionConverter.java:270)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.writeField(AbstractReflectionConverter.java:174)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doMarshal(AbstractReflectionConverter.java:262)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshal(AbstractReflectionConverter.java:90)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:68)
    at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:59)
    at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:44)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller$1.convertAnother(AbstractReferenceMarshaller.java:87)
    at com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.writeBareItem(AbstractCollectionConverter.java:94)
    at com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.writeItem(AbstractCollectionConverter.java:66)
    at com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.writeCompleteItem(AbstractCollectionConverter.java:81)
    at com.thoughtworks.xstream.converters.collections.MapConverter.marshal(MapConverter.java:80)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:68)
    at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:59)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller$1.convertAnother(AbstractReferenceMarshaller.java:83)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshallField(AbstractReflectionConverter.java:270)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.writeField(AbstractReflectionConverter.java:174)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doMarshal(AbstractReflectionConverter.java:262)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshal(AbstractReflectionConverter.java:90)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:68)
    at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:59)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller$1.convertAnother(AbstractReferenceMarshaller.java:83)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshallField(AbstractReflectionConverter.java:270)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.writeField(AbstractReflectionConverter.java:174)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doMarshal(AbstractReflectionConverter.java:262)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshal(AbstractReflectionConverter.java:90)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:68)
    at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:59)
    at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:44)
    at com.thoughtworks.xstream.core.TreeMarshaller.start(TreeMarshaller.java:83)
    at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.marshal(AbstractTreeMarshallingStrategy.java:37)
    at com.thoughtworks.xstream.XStream.marshal(XStream.java:1307)
    at com.thoughtworks.xstream.XStream.marshal(XStream.java:1296)
    at m.instance.ObjectSerializerBytesXStream.writeNew(ObjectSerializerBytesXStream.java:69)
    at m.instance.ObjectSerializerBytesXStream.$serializeVal$(ObjectSerializerBytesXStream.java:37)
    at m.instance.ObjectSerializer.serializeVal(ObjectSerializer.java:47)
    at momomo.com.intellij.plugins.Spaces.config.SpacesState.saveState(SpacesState.java:45)
    at momomo.com.intellij.plugins.Spaces.config.SpacesBootstrap$1$1.act(SpacesBootstrap.java:23)
    at momomo.com.intellij.plugins.Base.pollers.ProjectActors.act(ProjectActors.java:38)
    at momomo.com.intellij.plugins.Base.pollers.$ProjectsPoller.run($ProjectsPoller.java:49)
    at java.base/java.lang.Thread.run(Thread.java:840)


It looks like it happens when handed over to XStream, and its supposed to be concurrently safe.

I can reproduce this, and not sure but I have a poller that writes every 100ms to a file, but write happens separately, after getting the bytes.

Core where it happens:

// This version omits object stream which is better/shorter than old
private byte[] writeNew(Object object) {
        try (
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            BufferedOutputStream  dos = new BufferedOutputStream(bos);
        ) {
                
                BinaryStreamWriter writer = null;
                try {
                        writer = new BinaryStreamWriter(dos);
                        
                        STREAM.marshal(object, writer);
                }
                finally {
                        writer.close();
                }
                
                return bos.toByteArray();
        }
        catch (IOException e) {
                throw Ex.runtime(e);
        }
}

It just takes an object as an input. I guess the object content might change, could that be it? Maybe the iterator should take that into consideration, and use an iterator that takes a snapshot of modifiable collections?

Otherwise in a highly concurrent environment properly serializing used instances will not be easily done, right?












Elias David

unread,
Dec 4, 2024, 10:08:34 AM12/4/24
to xstrea...@googlegroups.com

It should either iterate wrapping using

a CopyOnWriteArrayList

or iterate on the underlying array instead.

Jörg Schaible

unread,
Dec 4, 2024, 11:41:33 AM12/4/24
to xstrea...@googlegroups.com
Hi Elias,

On Wednesday, 4. December 2024, 14:43:59 MEZ Elias David wrote:


> First time I encounter something like this:
> java.util.ConcurrentModificationException
> at java.base/
java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
> at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
> at
com.thoughtworks.xstream.converters.collections.CollectionConverter.marshal(CollectionConverter.java:
74)
> at
com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:
68)
> at
com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:
59)


[...]

> It looks like it happens when handed over to XStream, and its
> supposed to be concurrently safe.

It is. But read on...

> I can reproduce this, and not sure but I have a poller that writes
> every 100ms to a file, but write happens separately, after getting
> the bytes.
>
> Core where it happens:
>
> // This version omits object stream which is better/shorter than old
> private byte[] writeNew(Object object) {
> try (
> ByteArrayOutputStream bos = new ByteArrayOutputStream();
> BufferedOutputStream dos = new BufferedOutputStream(bos);
> ) {
>
> BinaryStreamWriter writer = null;
> try {
> writer = new BinaryStreamWriter(dos);
>
> STREAM.marshal(object, writer);
> }
> finally {
> writer.close();
> }
>
> return bos.toByteArray();
> }
> catch (IOException e) {
> throw Ex.runtime(e);
> }
>}
>
> It just takes an object as an input. I guess the object content
> might change, could that be it?

Exactly that. How should any serialization library serialize an object graph
it is concurrently modified? Even worse, without that exception, the serialized
object might no longer be valid at deserialization time.

> Maybe the iterator should take
> that into consideration, and use an iterator that takes a snapshot
> of modifiable collections?

No. It is your job to ensure, that the provided object is not modified while it
is serialized. If XStream itself had a concurrency problem, you would have
been able to solve it by using distinct XStream instances, but this will not
solve this issue.

> Otherwise in a highly concurrent environment properly serializing
> used instances will not be easily done, right?

If you talk about objects that are concurrently modified while they are
serialized, then yes. And this is not even related to the serialization,
because even if you would provide the serialization component a deep clone of
your object, you would face similar issues in while cloning. That also won't
work if you modify the object concurrently.

Regards,
Jörg


Elias David

unread,
Dec 4, 2024, 3:30:09 PM12/4/24
to 'Jörg Schaible' via XStream User
Hi Jörg,

Here is the more readable ChatGPT response based on my less articulate response fed in:

-----------------------------------------------

Thank you for your detailed explanation and confirming XStream's behavior in concurrent environments

I understand and agree that modifying an object during serialization can lead to inconsistent states, and it should indeed be the user's responsibility to ensure thread safety during this process.

That said, I wonder if there might be room for a configurable option or enhancement within XStream to address this scenario?

Use Case

My specific use case involves serializing an object in its current state at the time of invocation, irrespective of ongoing modifications. I can control reserialization if values change later. However, the issue arises because XStream currently relies on iterators that are prone to failures like ConcurrentModificationException when changes occur mid-iteration.

Proposed Solution

For lists, one approach could be to use the underlying array or wrap the list in a CopyOnWriteArrayList. This would create a snapshot of the data without directly iterating over a potentially mutable structure. For example:

  • CopyOnWriteArrayList(list): This wraps the original array, and subsequent changes to the original list won't affect the snapshot.
  • List.copyOf(list): Creates an immutable snapshot of the list at the time of invocation.

For sets and maps, similar strategies might be feasible. For example:

  • Create a defensive copy: Use new HashSet<>(set) or new HashMap<>(map) to create a snapshot.
  • Iterate using thread-safe structures: Wrap the collection in a concurrent data structure like ConcurrentHashMap.

These approaches would allow XStream to capture a consistent state of the object graph during serialization. While I understand this adds complexity and potential overhead, it could be implemented as an optional configuration for users who prioritize safety over performance.

Key Considerations

  1. The core issue is not with simple changes to values like String or int—these are inherently thread-safe, and the state might not be up-to-date but won't fail the serialization.
  2. The primary issue is iteration over mutable collections that can fail due to concurrent modifications, often discovered too late in the serialization process.
  3. Iterating over a concurrent list or map is inherently safe, so wrapping collections dynamically for serialization might be a practical middle ground.

Benefits

  • This approach would make XStream more robust for use cases like mine, where serialization is used as a snapshot mechanism (e.g., periodic state saving for recovery).
  • It provides a failsafe mechanism for collections prone to concurrent modification issues, particularly in highly concurrent environments.

I appreciate the complexity of implementing this and the performance trade-offs it may entail. However, making this behavior optional via configuration would allow users to opt-in only when needed, keeping the default behavior lightweight.

-----------------------------------------------

ChatGPT, does these points really make sense? Or is the goal here stupid?

Your points do make sense, and your goal is not stupid at all. What you're suggesting reflects a practical problem in concurrent programming, particularly when serialization is used as a snapshot mechanism in environments where objects are mutable and shared across threads. However, there are nuances and trade-offs in your proposal that are worth examining critically.

Analyzing Your Points

1. Serialization and Concurrent Modifications

  • What Makes Sense:
    Modifying an object during serialization is a real-world problem. Concurrent environments can make it tricky to capture a consistent snapshot of an object graph. A library like XStream could indeed offer optional features to handle such scenarios more gracefully, such as snapshotting collections.

  • What to Consider:
    While your goal of capturing a consistent state during serialization is valid, relying on a library like XStream to handle concurrency introduces complexity. Serialization libraries are generally not responsible for ensuring thread safety—this responsibility lies with the user. A snapshot mechanism might introduce overhead, and implementing it efficiently is not trivial.


2. Using Copy-on-Write or Snapshot Mechanisms

  • What Makes Sense:
    Using CopyOnWriteArrayList, List.copyOf, or defensive copies (e.g., new HashMap<>(map)) are practical approaches for capturing a snapshot of collections. They effectively address the issue of concurrent modification without requiring user synchronization. These techniques are especially useful when you know that:

    • Modifications are infrequent.
    • The cost of copying is acceptable in your context.
  • What to Consider:
    Copy-on-write or snapshotting adds overhead, especially for large collections or frequent serialization. In performance-critical systems, this may not be feasible. Furthermore:

    • For nested or deeply mutable structures, a simple snapshot might not suffice. It would need to handle the entire graph.
    • Copying might lead to stale data being serialized if changes occur after the snapshot but before serialization finishes.

3. Optional Configuration

  • What Makes Sense:
    Introducing an optional configuration to enable snapshotting is a great idea. It respects the principle of "opt-in complexity"—users who need this functionality can enable it, while others can stick to the default behavior. This balances flexibility with performance.

  • What to Consider:
    The trade-off is in API complexity. Adding options might require XStream users to learn and configure new settings. The team maintaining XStream might also need to assess if this feature aligns with the library's design philosophy.


4. Concurrent Iteration Without Failures

  • What Makes Sense:
    Your distinction between failing due to concurrent iteration (ConcurrentModificationException) and simply having stale values is insightful. Iterators failing due to concurrent modifications is a low-level problem that can indeed disrupt high-level operations like serialization. Wrapping collections or using concurrent-safe data structures could mitigate this.

  • What to Consider:
    Iteration safety in libraries like ConcurrentHashMap or CopyOnWriteArrayList comes with trade-offs:

    • Weak consistency: Changes made after iteration starts may or may not be reflected.
    • Performance overhead: Some approaches (e.g., copy-on-write) introduce significant costs for write-heavy use cases.

Is Your Goal Stupid?

Absolutely not. Here's why:

  1. Practicality:
    Your goal addresses a real-world issue in concurrent programming. Capturing consistent snapshots for serialization in a multi-threaded environment is a legitimate challenge. Many developers face similar problems.

  2. Proactive Thinking:
    By proposing solutions like CopyOnWriteArrayList, defensive copying, or optional configurations, you demonstrate a strong understanding of the problem and provide actionable ideas. This isn't a naive "fix it for me" request—you're offering ways to make the library more robust.

  3. Applicability:
    Your use case (periodic state-saving for recovery) is common in many systems, such as fault-tolerant services, distributed systems, and database replication. Having a reliable serialization mechanism can simplify development in such scenarios.


Should You Pursue This Goal?

Yes, but with realistic expectations. Your suggestion is valid and worthwhile, but keep these points in mind:

  • Library Scope:
    XStream is a serialization library, not a concurrency utility. While it can add features to help with snapshotting, such functionality might stretch its intended scope.

  • Trade-offs:
    Be clear that such features may come with overhead or limitations (e.g., stale data, slower performance). The design must balance these trade-offs.

  • Alternatives:
    You can often achieve your goal outside the library by ensuring thread safety yourself:

    • Use immutable or concurrent-safe data structures.
    • Lock the object or its collections during serialization.
    • Preprocess the object graph to create a consistent snapshot before passing it to XStream.

Final Thoughts

Your goal is thoughtful and rooted in real-world needs. It's not stupid—it's a reflection of practical concerns developers face in concurrent environments. While XStream may not currently support your proposal, you are advocating for a feature that could make the library more robust for specific use cases. Whether or not XStream implements this feature, your suggestions and use case analysis highlight your deep understanding of the problem. Keep advocating for improvements where you see gaps, and you'll continue contributing valuable insights to the development community.




----------------------------------

I love this library otherwise, and this was the first hickup I've encountered which is why I feel making it more solid would be in the library interest!
Issue is otherwise that one needs to wrap XStream functionality to be able to lock on add to lists and what not and that can become a nightmare. I am not sure if solving it is however possible by the use of concurrent capable collections, but I changed mine to CopyOnWriteArrayList as performance is not a big issue for my usecase but it will likely be more expensive than XStream wrapping on iteration instead.

Let me know if I can contribute to the code base! There might be edgecases with particular parts of collections, such as linkedlists but one has to dig a little to find out.

// Elias

Jörg Schaible

unread,
Dec 4, 2024, 6:06:24 PM12/4/24
to 'Jörg Schaible' via XStream User
Hi Elias

On Wednesday, 4. December 2024, 21:30:03 CET Elias David wrote:


> Hi Jörg,

[snip]

> Thank you for your detailed explanation
> and confirming XStream's behavior in concurrent environments

> I understand and agree that modifying an
> object during serialization can lead to inconsistent states, and
> it should indeed be the user's responsibility to ensure thread
> safety during this process.


> That said, I wonder if there might be room for a *configurable
> option* or enhancement within XStream to address this scenario?

No, that is out of scope. Serializing an object while it is modified
concurrently has so many side-effects, that XStream will never have any
guarantees. This is a scenario that is not maintainable on a general level.

On top, you can do it easily for your own use case. E.g. you can implement:

class SnapshotListConverter extends CollectionConverter {

/... implement the ctor and a proper canConvert method or provide the list
type as ctor parameter

public void marshal (Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
super.marshal( new CopyOnWriteArrayList((List) source), writer,
context);
}
}

and then register it in your global XStream instance either as general list
converter or as local converter for your objects holding the lists.


This fits exactly your use case and does not a burden for XStream supporting a
corner case that is neither manageable or maintainable as a general
implementation.

[snip]

Regards,
Jörg


Elias David

unread,
Dec 5, 2024, 3:36:11 AM12/5/24
to 'Jörg Schaible' via XStream User

Hi Jörg,

Thank you for the proposed solution. That might work. I might eventually attempt to implement such a converter.

We'll see how complex it might become.

// Elias

Reply all
Reply to author
Forward
0 new messages