This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new 9a12400ee fix(java): Fix CopyOnWriteArrayList field serialization 
(#3079)
9a12400ee is described below

commit 9a12400eeadb7ebc2d1ea3a132e77fec7f4768a9
Author: Vybhav Jayasankar <[email protected]>
AuthorDate: Tue Dec 23 21:49:09 2025 +0530

    fix(java): Fix CopyOnWriteArrayList field serialization (#3079)
    
    ## Why?
    
    Fory doesn't currently support serialization of any object that has a
    `CopyOnWriteArrayList` field with codegen, because the generated code
    attempts to cast `CollectionSnapshot` to `List`, throwing the following
    exception :
    
    ```
    org.apache.fory.exception.SerializationException: 
java.lang.ClassCastException:
    class org.apache.fory.collection.CollectionSnapshot cannot be cast to class 
java.util.List
    (org.apache.fory.collection.CollectionSnapshot is in unnamed module of 
loader 'app';
    java.util.List is in module java.base of loader 'bootstrap')
    ( . . . )
    
org.apache.fory.serializer.collection.CollectionSerializersTest.testCopyOnWriteArrayListNested(CollectionSerializersTest.java:407)
    Caused by: java.lang.ClassCastException: class 
org.apache.fory.collection.CollectionSnapshot cannot be cast to class 
java.util.List
    (org.apache.fory.collection.CollectionSnapshot is in unnamed module of 
loader 'app';
    java.util.List is in module java.base of loader 'bootstrap')
            at 
org.apache.fory.serializer.collection.CollectionSerializersTest_NestedCopyOnWriteArrayListForyCodec_0.writeFields$(CollectionSerializersTest_NestedCopyOnWriteArrayListForyCodec_0.java:63)
            at 
org.apache.fory.serializer.collection.CollectionSerializersTest_NestedCopyOnWriteArrayListForyCodec_0.write(CollectionSerializersTest_NestedCopyOnWriteArrayListForyCodec_0.java:130)
    ```
    
    This is the relevant code from the generated codec that attempts the
    invalid class cast.
    
    ```
    CollectionLikeSerializer collectionLikeSerializer = 
this.writeCollectionClassInfo(list0, memoryBuffer1);
    if (collectionLikeSerializer.supportCodegenHook()) {
        java.util.List list1 = 
(java.util.List)collectionLikeSerializer.onCollectionWrite(memoryBuffer1, 
list0);
    ```
    
    ## What does this PR do?
    
    * feat: Update `CollectionSnapshot` to extend `AbstractList` instead of
    `AbstractCollection`, and implement `get(index)` method
    * test: Add test in `CollectionSerializersTest` for serializing objects
    with CopyOnWriteArrayList fields
    * test: Add test in `CollectionSnapshotTest` for
    `CollectionSnapshot.get(index)`
    
    ## Related issues
    
    Fixes pending issues from #2918.
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 .../apache/fory/collection/CollectionSnapshot.java |  9 +++++--
 .../fory/collection/CollectionSnapshotTest.java    | 30 ++++++++++++++++++++++
 .../collection/CollectionSerializersTest.java      | 18 +++++++++++++
 3 files changed, 55 insertions(+), 2 deletions(-)

diff --git 
a/java/fory-core/src/main/java/org/apache/fory/collection/CollectionSnapshot.java
 
b/java/fory-core/src/main/java/org/apache/fory/collection/CollectionSnapshot.java
index 4f15a909c..de8aae70c 100644
--- 
a/java/fory-core/src/main/java/org/apache/fory/collection/CollectionSnapshot.java
+++ 
b/java/fory-core/src/main/java/org/apache/fory/collection/CollectionSnapshot.java
@@ -19,7 +19,7 @@
 
 package org.apache.fory.collection;
 
-import java.util.AbstractCollection;
+import java.util.AbstractList;
 import java.util.Collection;
 import java.util.Iterator;
 import org.apache.fory.annotation.Internal;
@@ -46,7 +46,7 @@ import org.apache.fory.annotation.Internal;
  * @since 1.0
  */
 @Internal
-public class CollectionSnapshot<E> extends AbstractCollection<E> {
+public class CollectionSnapshot<E> extends AbstractList<E> {
   /** Threshold for array reallocation during clear operation to prevent 
memory leaks. */
   private static final int CLEAR_ARRAY_SIZE_THRESHOLD = 2048;
 
@@ -79,6 +79,11 @@ public class CollectionSnapshot<E> extends 
AbstractCollection<E> {
     return size;
   }
 
+  @Override
+  public E get(int index) {
+    return array.get(index);
+  }
+
   /**
    * Returns an iterator over the elements in this collection. The iterator is 
designed for
    * single-pass iteration and maintains its position through a shared index.
diff --git 
a/java/fory-core/src/test/java/org/apache/fory/collection/CollectionSnapshotTest.java
 
b/java/fory-core/src/test/java/org/apache/fory/collection/CollectionSnapshotTest.java
index e0e05bd6b..de1c536de 100644
--- 
a/java/fory-core/src/test/java/org/apache/fory/collection/CollectionSnapshotTest.java
+++ 
b/java/fory-core/src/test/java/org/apache/fory/collection/CollectionSnapshotTest.java
@@ -107,4 +107,34 @@ public class CollectionSnapshotTest {
       assertEquals(result, newData);
     }
   }
+
+  @Test
+  public void testGet() {
+    CollectionSnapshot<String> snapshot = new CollectionSnapshot<>();
+    List<String> source = Arrays.asList("a", "b", "c");
+    snapshot.setCollection(source);
+
+    assertEquals(snapshot.get(0), "a");
+    assertEquals(snapshot.get(1), "b");
+    assertEquals(snapshot.get(2), "c");
+  }
+
+  @Test
+  public void testIndexedAccessAfterReuse() {
+    CollectionSnapshot<String> snapshot = new CollectionSnapshot<>();
+
+    // First use
+    snapshot.setCollection(Arrays.asList("a", "b", "c"));
+    assertEquals(snapshot.get(0), "a");
+    assertEquals(snapshot.get(1), "b");
+    assertEquals(snapshot.get(2), "c");
+
+    snapshot.clear();
+
+    // Second use
+    snapshot.setCollection(Arrays.asList("x", "y"));
+    assertEquals(snapshot.get(0), "x");
+    assertEquals(snapshot.get(1), "y");
+    assertEquals(snapshot.size(), 2);
+  }
 }
diff --git 
a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java
 
b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java
index 59f12386f..607f1087a 100644
--- 
a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java
+++ 
b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java
@@ -62,6 +62,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.LongStream;
 import lombok.AllArgsConstructor;
 import lombok.Data;
+import lombok.EqualsAndHashCode;
 import org.apache.fory.Fory;
 import org.apache.fory.ForyTestBase;
 import org.apache.fory.config.Language;
@@ -389,6 +390,23 @@ public class CollectionSerializersTest extends 
ForyTestBase {
         CollectionSerializers.CopyOnWriteArrayListSerializer.class);
   }
 
+  @EqualsAndHashCode
+  public static class NestedCopyOnWriteArrayList {
+    private final CopyOnWriteArrayList<String> list;
+
+    public NestedCopyOnWriteArrayList(CopyOnWriteArrayList<String> list) {
+      this.list = list;
+    }
+  }
+
+  @Test
+  public void testCopyOnWriteArrayListNested() {
+    final CopyOnWriteArrayList<String> list =
+        new CopyOnWriteArrayList<>(Arrays.asList("a", "b", "c"));
+    NestedCopyOnWriteArrayList nestedObject = new 
NestedCopyOnWriteArrayList(list);
+    Assert.assertEquals(nestedObject, serDe(getJavaFory(), nestedObject));
+  }
+
   @Test(dataProvider = "foryCopyConfig")
   public void testCopyOnWriteArrayList(Fory fory) {
     final CopyOnWriteArrayList<Object> list =


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to